1*1b3f573fSAndroid Build Coastguard Worker #region Copyright notice and license 2*1b3f573fSAndroid Build Coastguard Worker // Protocol Buffers - Google's data interchange format 3*1b3f573fSAndroid Build Coastguard Worker // Copyright 2008 Google Inc. All rights reserved. 4*1b3f573fSAndroid Build Coastguard Worker // https://developers.google.com/protocol-buffers/ 5*1b3f573fSAndroid Build Coastguard Worker // 6*1b3f573fSAndroid Build Coastguard Worker // Redistribution and use in source and binary forms, with or without 7*1b3f573fSAndroid Build Coastguard Worker // modification, are permitted provided that the following conditions are 8*1b3f573fSAndroid Build Coastguard Worker // met: 9*1b3f573fSAndroid Build Coastguard Worker // 10*1b3f573fSAndroid Build Coastguard Worker // * Redistributions of source code must retain the above copyright 11*1b3f573fSAndroid Build Coastguard Worker // notice, this list of conditions and the following disclaimer. 12*1b3f573fSAndroid Build Coastguard Worker // * Redistributions in binary form must reproduce the above 13*1b3f573fSAndroid Build Coastguard Worker // copyright notice, this list of conditions and the following disclaimer 14*1b3f573fSAndroid Build Coastguard Worker // in the documentation and/or other materials provided with the 15*1b3f573fSAndroid Build Coastguard Worker // distribution. 16*1b3f573fSAndroid Build Coastguard Worker // * Neither the name of Google Inc. nor the names of its 17*1b3f573fSAndroid Build Coastguard Worker // contributors may be used to endorse or promote products derived from 18*1b3f573fSAndroid Build Coastguard Worker // this software without specific prior written permission. 19*1b3f573fSAndroid Build Coastguard Worker // 20*1b3f573fSAndroid Build Coastguard Worker // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21*1b3f573fSAndroid Build Coastguard Worker // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23*1b3f573fSAndroid Build Coastguard Worker // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24*1b3f573fSAndroid Build Coastguard Worker // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25*1b3f573fSAndroid Build Coastguard Worker // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27*1b3f573fSAndroid Build Coastguard Worker // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28*1b3f573fSAndroid Build Coastguard Worker // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29*1b3f573fSAndroid Build Coastguard Worker // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30*1b3f573fSAndroid Build Coastguard Worker // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31*1b3f573fSAndroid Build Coastguard Worker #endregion 32*1b3f573fSAndroid Build Coastguard Worker 33*1b3f573fSAndroid Build Coastguard Worker using System; 34*1b3f573fSAndroid Build Coastguard Worker using System.Buffers.Binary; 35*1b3f573fSAndroid Build Coastguard Worker using System.Diagnostics; 36*1b3f573fSAndroid Build Coastguard Worker using System.Runtime.CompilerServices; 37*1b3f573fSAndroid Build Coastguard Worker using System.Runtime.InteropServices; 38*1b3f573fSAndroid Build Coastguard Worker #if GOOGLE_PROTOBUF_SIMD 39*1b3f573fSAndroid Build Coastguard Worker using System.Runtime.Intrinsics; 40*1b3f573fSAndroid Build Coastguard Worker using System.Runtime.Intrinsics.Arm; 41*1b3f573fSAndroid Build Coastguard Worker using System.Runtime.Intrinsics.X86; 42*1b3f573fSAndroid Build Coastguard Worker #endif 43*1b3f573fSAndroid Build Coastguard Worker using System.Security; 44*1b3f573fSAndroid Build Coastguard Worker using System.Text; 45*1b3f573fSAndroid Build Coastguard Worker 46*1b3f573fSAndroid Build Coastguard Worker namespace Google.Protobuf 47*1b3f573fSAndroid Build Coastguard Worker { 48*1b3f573fSAndroid Build Coastguard Worker /// <summary> 49*1b3f573fSAndroid Build Coastguard Worker /// Primitives for encoding protobuf wire format. 50*1b3f573fSAndroid Build Coastguard Worker /// </summary> 51*1b3f573fSAndroid Build Coastguard Worker [SecuritySafeCritical] 52*1b3f573fSAndroid Build Coastguard Worker internal static class WritingPrimitives 53*1b3f573fSAndroid Build Coastguard Worker { 54*1b3f573fSAndroid Build Coastguard Worker #if NET5_0 55*1b3f573fSAndroid Build Coastguard Worker internal static Encoding Utf8Encoding => Encoding.UTF8; // allows JIT to devirtualize 56*1b3f573fSAndroid Build Coastguard Worker #else 57*1b3f573fSAndroid Build Coastguard Worker internal static readonly Encoding Utf8Encoding = Encoding.UTF8; // "Local" copy of Encoding.UTF8, for efficiency. (Yes, it makes a difference.) 58*1b3f573fSAndroid Build Coastguard Worker #endif 59*1b3f573fSAndroid Build Coastguard Worker 60*1b3f573fSAndroid Build Coastguard Worker #region Writing of values (not including tags) 61*1b3f573fSAndroid Build Coastguard Worker 62*1b3f573fSAndroid Build Coastguard Worker /// <summary> 63*1b3f573fSAndroid Build Coastguard Worker /// Writes a double field value, without a tag, to the stream. 64*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteDouble(ref Span<byte> buffer, ref WriterInternalState state, double value)65*1b3f573fSAndroid Build Coastguard Worker public static void WriteDouble(ref Span<byte> buffer, ref WriterInternalState state, double value) 66*1b3f573fSAndroid Build Coastguard Worker { 67*1b3f573fSAndroid Build Coastguard Worker WriteRawLittleEndian64(ref buffer, ref state, (ulong)BitConverter.DoubleToInt64Bits(value)); 68*1b3f573fSAndroid Build Coastguard Worker } 69*1b3f573fSAndroid Build Coastguard Worker 70*1b3f573fSAndroid Build Coastguard Worker /// <summary> 71*1b3f573fSAndroid Build Coastguard Worker /// Writes a float field value, without a tag, to the stream. 72*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteFloat(ref Span<byte> buffer, ref WriterInternalState state, float value)73*1b3f573fSAndroid Build Coastguard Worker public static unsafe void WriteFloat(ref Span<byte> buffer, ref WriterInternalState state, float value) 74*1b3f573fSAndroid Build Coastguard Worker { 75*1b3f573fSAndroid Build Coastguard Worker const int length = sizeof(float); 76*1b3f573fSAndroid Build Coastguard Worker if (buffer.Length - state.position >= length) 77*1b3f573fSAndroid Build Coastguard Worker { 78*1b3f573fSAndroid Build Coastguard Worker // if there's enough space in the buffer, write the float directly into the buffer 79*1b3f573fSAndroid Build Coastguard Worker var floatSpan = buffer.Slice(state.position, length); 80*1b3f573fSAndroid Build Coastguard Worker Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(floatSpan), value); 81*1b3f573fSAndroid Build Coastguard Worker 82*1b3f573fSAndroid Build Coastguard Worker if (!BitConverter.IsLittleEndian) 83*1b3f573fSAndroid Build Coastguard Worker { 84*1b3f573fSAndroid Build Coastguard Worker floatSpan.Reverse(); 85*1b3f573fSAndroid Build Coastguard Worker } 86*1b3f573fSAndroid Build Coastguard Worker state.position += length; 87*1b3f573fSAndroid Build Coastguard Worker } 88*1b3f573fSAndroid Build Coastguard Worker else 89*1b3f573fSAndroid Build Coastguard Worker { 90*1b3f573fSAndroid Build Coastguard Worker WriteFloatSlowPath(ref buffer, ref state, value); 91*1b3f573fSAndroid Build Coastguard Worker } 92*1b3f573fSAndroid Build Coastguard Worker } 93*1b3f573fSAndroid Build Coastguard Worker 94*1b3f573fSAndroid Build Coastguard Worker [MethodImpl(MethodImplOptions.NoInlining)] WriteFloatSlowPath(ref Span<byte> buffer, ref WriterInternalState state, float value)95*1b3f573fSAndroid Build Coastguard Worker private static unsafe void WriteFloatSlowPath(ref Span<byte> buffer, ref WriterInternalState state, float value) 96*1b3f573fSAndroid Build Coastguard Worker { 97*1b3f573fSAndroid Build Coastguard Worker const int length = sizeof(float); 98*1b3f573fSAndroid Build Coastguard Worker 99*1b3f573fSAndroid Build Coastguard Worker // TODO(jtattermusch): deduplicate the code. Populating the span is the same as for the fastpath. 100*1b3f573fSAndroid Build Coastguard Worker Span<byte> floatSpan = stackalloc byte[length]; 101*1b3f573fSAndroid Build Coastguard Worker Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(floatSpan), value); 102*1b3f573fSAndroid Build Coastguard Worker if (!BitConverter.IsLittleEndian) 103*1b3f573fSAndroid Build Coastguard Worker { 104*1b3f573fSAndroid Build Coastguard Worker floatSpan.Reverse(); 105*1b3f573fSAndroid Build Coastguard Worker } 106*1b3f573fSAndroid Build Coastguard Worker 107*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, floatSpan[0]); 108*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, floatSpan[1]); 109*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, floatSpan[2]); 110*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, floatSpan[3]); 111*1b3f573fSAndroid Build Coastguard Worker } 112*1b3f573fSAndroid Build Coastguard Worker 113*1b3f573fSAndroid Build Coastguard Worker /// <summary> 114*1b3f573fSAndroid Build Coastguard Worker /// Writes a uint64 field value, without a tag, to the stream. 115*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteUInt64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)116*1b3f573fSAndroid Build Coastguard Worker public static void WriteUInt64(ref Span<byte> buffer, ref WriterInternalState state, ulong value) 117*1b3f573fSAndroid Build Coastguard Worker { 118*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint64(ref buffer, ref state, value); 119*1b3f573fSAndroid Build Coastguard Worker } 120*1b3f573fSAndroid Build Coastguard Worker 121*1b3f573fSAndroid Build Coastguard Worker /// <summary> 122*1b3f573fSAndroid Build Coastguard Worker /// Writes an int64 field value, without a tag, to the stream. 123*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteInt64(ref Span<byte> buffer, ref WriterInternalState state, long value)124*1b3f573fSAndroid Build Coastguard Worker public static void WriteInt64(ref Span<byte> buffer, ref WriterInternalState state, long value) 125*1b3f573fSAndroid Build Coastguard Worker { 126*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint64(ref buffer, ref state, (ulong)value); 127*1b3f573fSAndroid Build Coastguard Worker } 128*1b3f573fSAndroid Build Coastguard Worker 129*1b3f573fSAndroid Build Coastguard Worker /// <summary> 130*1b3f573fSAndroid Build Coastguard Worker /// Writes an int32 field value, without a tag, to the stream. 131*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteInt32(ref Span<byte> buffer, ref WriterInternalState state, int value)132*1b3f573fSAndroid Build Coastguard Worker public static void WriteInt32(ref Span<byte> buffer, ref WriterInternalState state, int value) 133*1b3f573fSAndroid Build Coastguard Worker { 134*1b3f573fSAndroid Build Coastguard Worker if (value >= 0) 135*1b3f573fSAndroid Build Coastguard Worker { 136*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint32(ref buffer, ref state, (uint)value); 137*1b3f573fSAndroid Build Coastguard Worker } 138*1b3f573fSAndroid Build Coastguard Worker else 139*1b3f573fSAndroid Build Coastguard Worker { 140*1b3f573fSAndroid Build Coastguard Worker // Must sign-extend. 141*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint64(ref buffer, ref state, (ulong)value); 142*1b3f573fSAndroid Build Coastguard Worker } 143*1b3f573fSAndroid Build Coastguard Worker } 144*1b3f573fSAndroid Build Coastguard Worker 145*1b3f573fSAndroid Build Coastguard Worker /// <summary> 146*1b3f573fSAndroid Build Coastguard Worker /// Writes a fixed64 field value, without a tag, to the stream. 147*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteFixed64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)148*1b3f573fSAndroid Build Coastguard Worker public static void WriteFixed64(ref Span<byte> buffer, ref WriterInternalState state, ulong value) 149*1b3f573fSAndroid Build Coastguard Worker { 150*1b3f573fSAndroid Build Coastguard Worker WriteRawLittleEndian64(ref buffer, ref state, value); 151*1b3f573fSAndroid Build Coastguard Worker } 152*1b3f573fSAndroid Build Coastguard Worker 153*1b3f573fSAndroid Build Coastguard Worker /// <summary> 154*1b3f573fSAndroid Build Coastguard Worker /// Writes a fixed32 field value, without a tag, to the stream. 155*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteFixed32(ref Span<byte> buffer, ref WriterInternalState state, uint value)156*1b3f573fSAndroid Build Coastguard Worker public static void WriteFixed32(ref Span<byte> buffer, ref WriterInternalState state, uint value) 157*1b3f573fSAndroid Build Coastguard Worker { 158*1b3f573fSAndroid Build Coastguard Worker WriteRawLittleEndian32(ref buffer, ref state, value); 159*1b3f573fSAndroid Build Coastguard Worker } 160*1b3f573fSAndroid Build Coastguard Worker 161*1b3f573fSAndroid Build Coastguard Worker /// <summary> 162*1b3f573fSAndroid Build Coastguard Worker /// Writes a bool field value, without a tag, to the stream. 163*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteBool(ref Span<byte> buffer, ref WriterInternalState state, bool value)164*1b3f573fSAndroid Build Coastguard Worker public static void WriteBool(ref Span<byte> buffer, ref WriterInternalState state, bool value) 165*1b3f573fSAndroid Build Coastguard Worker { 166*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, value ? (byte)1 : (byte)0); 167*1b3f573fSAndroid Build Coastguard Worker } 168*1b3f573fSAndroid Build Coastguard Worker 169*1b3f573fSAndroid Build Coastguard Worker /// <summary> 170*1b3f573fSAndroid Build Coastguard Worker /// Writes a string field value, without a tag, to the stream. 171*1b3f573fSAndroid Build Coastguard Worker /// The data is length-prefixed. 172*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteString(ref Span<byte> buffer, ref WriterInternalState state, string value)173*1b3f573fSAndroid Build Coastguard Worker public static void WriteString(ref Span<byte> buffer, ref WriterInternalState state, string value) 174*1b3f573fSAndroid Build Coastguard Worker { 175*1b3f573fSAndroid Build Coastguard Worker const int MaxBytesPerChar = 3; 176*1b3f573fSAndroid Build Coastguard Worker const int MaxSmallStringLength = 128 / MaxBytesPerChar; 177*1b3f573fSAndroid Build Coastguard Worker 178*1b3f573fSAndroid Build Coastguard Worker // The string is small enough that the length will always be a 1 byte varint. 179*1b3f573fSAndroid Build Coastguard Worker // Also there is enough space to write length + bytes to buffer. 180*1b3f573fSAndroid Build Coastguard Worker // Write string directly to the buffer, and then write length. 181*1b3f573fSAndroid Build Coastguard Worker // This saves calling GetByteCount on the string. We get the string length from GetBytes. 182*1b3f573fSAndroid Build Coastguard Worker if (value.Length <= MaxSmallStringLength && buffer.Length - state.position - 1 >= value.Length * MaxBytesPerChar) 183*1b3f573fSAndroid Build Coastguard Worker { 184*1b3f573fSAndroid Build Coastguard Worker int indexOfLengthDelimiter = state.position++; 185*1b3f573fSAndroid Build Coastguard Worker buffer[indexOfLengthDelimiter] = (byte)WriteStringToBuffer(buffer, ref state, value); 186*1b3f573fSAndroid Build Coastguard Worker return; 187*1b3f573fSAndroid Build Coastguard Worker } 188*1b3f573fSAndroid Build Coastguard Worker 189*1b3f573fSAndroid Build Coastguard Worker int length = Utf8Encoding.GetByteCount(value); 190*1b3f573fSAndroid Build Coastguard Worker WriteLength(ref buffer, ref state, length); 191*1b3f573fSAndroid Build Coastguard Worker 192*1b3f573fSAndroid Build Coastguard Worker // Optimise the case where we have enough space to write 193*1b3f573fSAndroid Build Coastguard Worker // the string directly to the buffer, which should be common. 194*1b3f573fSAndroid Build Coastguard Worker if (buffer.Length - state.position >= length) 195*1b3f573fSAndroid Build Coastguard Worker { 196*1b3f573fSAndroid Build Coastguard Worker if (length == value.Length) // Must be all ASCII... 197*1b3f573fSAndroid Build Coastguard Worker { 198*1b3f573fSAndroid Build Coastguard Worker WriteAsciiStringToBuffer(buffer, ref state, value, length); 199*1b3f573fSAndroid Build Coastguard Worker } 200*1b3f573fSAndroid Build Coastguard Worker else 201*1b3f573fSAndroid Build Coastguard Worker { 202*1b3f573fSAndroid Build Coastguard Worker WriteStringToBuffer(buffer, ref state, value); 203*1b3f573fSAndroid Build Coastguard Worker } 204*1b3f573fSAndroid Build Coastguard Worker } 205*1b3f573fSAndroid Build Coastguard Worker else 206*1b3f573fSAndroid Build Coastguard Worker { 207*1b3f573fSAndroid Build Coastguard Worker // Opportunity for future optimization: 208*1b3f573fSAndroid Build Coastguard Worker // Large strings that don't fit into the current buffer segment 209*1b3f573fSAndroid Build Coastguard Worker // can probably be optimized by using Utf8Encoding.GetEncoder() 210*1b3f573fSAndroid Build Coastguard Worker // but more benchmarks would need to be added as evidence. 211*1b3f573fSAndroid Build Coastguard Worker byte[] bytes = Utf8Encoding.GetBytes(value); 212*1b3f573fSAndroid Build Coastguard Worker WriteRawBytes(ref buffer, ref state, bytes); 213*1b3f573fSAndroid Build Coastguard Worker } 214*1b3f573fSAndroid Build Coastguard Worker } 215*1b3f573fSAndroid Build Coastguard Worker 216*1b3f573fSAndroid Build Coastguard Worker // Calling this method with non-ASCII content will break. 217*1b3f573fSAndroid Build Coastguard Worker // Content must be verified to be all ASCII before using this method. WriteAsciiStringToBuffer(Span<byte> buffer, ref WriterInternalState state, string value, int length)218*1b3f573fSAndroid Build Coastguard Worker private static void WriteAsciiStringToBuffer(Span<byte> buffer, ref WriterInternalState state, string value, int length) 219*1b3f573fSAndroid Build Coastguard Worker { 220*1b3f573fSAndroid Build Coastguard Worker ref char sourceChars = ref MemoryMarshal.GetReference(value.AsSpan()); 221*1b3f573fSAndroid Build Coastguard Worker ref byte destinationBytes = ref MemoryMarshal.GetReference(buffer.Slice(state.position)); 222*1b3f573fSAndroid Build Coastguard Worker 223*1b3f573fSAndroid Build Coastguard Worker int currentIndex = 0; 224*1b3f573fSAndroid Build Coastguard Worker // If 64bit, process 4 chars at a time. 225*1b3f573fSAndroid Build Coastguard Worker // The logic inside this check will be elided by JIT in 32bit programs. 226*1b3f573fSAndroid Build Coastguard Worker if (IntPtr.Size == 8) 227*1b3f573fSAndroid Build Coastguard Worker { 228*1b3f573fSAndroid Build Coastguard Worker // Need at least 4 chars available to use this optimization. 229*1b3f573fSAndroid Build Coastguard Worker if (length >= 4) 230*1b3f573fSAndroid Build Coastguard Worker { 231*1b3f573fSAndroid Build Coastguard Worker ref byte sourceBytes = ref Unsafe.As<char, byte>(ref sourceChars); 232*1b3f573fSAndroid Build Coastguard Worker 233*1b3f573fSAndroid Build Coastguard Worker // Process 4 chars at a time until there are less than 4 remaining. 234*1b3f573fSAndroid Build Coastguard Worker // We already know all characters are ASCII so there is no need to validate the source. 235*1b3f573fSAndroid Build Coastguard Worker int lastIndexWhereCanReadFourChars = value.Length - 4; 236*1b3f573fSAndroid Build Coastguard Worker do 237*1b3f573fSAndroid Build Coastguard Worker { 238*1b3f573fSAndroid Build Coastguard Worker NarrowFourUtf16CharsToAsciiAndWriteToBuffer( 239*1b3f573fSAndroid Build Coastguard Worker ref Unsafe.AddByteOffset(ref destinationBytes, (IntPtr)currentIndex), 240*1b3f573fSAndroid Build Coastguard Worker Unsafe.ReadUnaligned<ulong>(ref Unsafe.AddByteOffset(ref sourceBytes, (IntPtr)(currentIndex * 2)))); 241*1b3f573fSAndroid Build Coastguard Worker 242*1b3f573fSAndroid Build Coastguard Worker } while ((currentIndex += 4) <= lastIndexWhereCanReadFourChars); 243*1b3f573fSAndroid Build Coastguard Worker } 244*1b3f573fSAndroid Build Coastguard Worker } 245*1b3f573fSAndroid Build Coastguard Worker 246*1b3f573fSAndroid Build Coastguard Worker // Process any remaining, 1 char at a time. 247*1b3f573fSAndroid Build Coastguard Worker // Avoid bounds checking with ref + Unsafe 248*1b3f573fSAndroid Build Coastguard Worker for (; currentIndex < length; currentIndex++) 249*1b3f573fSAndroid Build Coastguard Worker { 250*1b3f573fSAndroid Build Coastguard Worker Unsafe.AddByteOffset(ref destinationBytes, (IntPtr)currentIndex) = (byte)Unsafe.AddByteOffset(ref sourceChars, (IntPtr)(currentIndex * 2)); 251*1b3f573fSAndroid Build Coastguard Worker } 252*1b3f573fSAndroid Build Coastguard Worker 253*1b3f573fSAndroid Build Coastguard Worker state.position += length; 254*1b3f573fSAndroid Build Coastguard Worker } 255*1b3f573fSAndroid Build Coastguard Worker 256*1b3f573fSAndroid Build Coastguard Worker // Copied with permission from https://github.com/dotnet/runtime/blob/1cdafd27e4afd2c916af5df949c13f8b373c4335/src/libraries/System.Private.CoreLib/src/System/Text/ASCIIUtility.cs#L1119-L1171 257*1b3f573fSAndroid Build Coastguard Worker // 258*1b3f573fSAndroid Build Coastguard Worker /// <summary> 259*1b3f573fSAndroid Build Coastguard Worker /// Given a QWORD which represents a buffer of 4 ASCII chars in machine-endian order, 260*1b3f573fSAndroid Build Coastguard Worker /// narrows each WORD to a BYTE, then writes the 4-byte result to the output buffer 261*1b3f573fSAndroid Build Coastguard Worker /// also in machine-endian order. 262*1b3f573fSAndroid Build Coastguard Worker /// </summary> 263*1b3f573fSAndroid Build Coastguard Worker [MethodImpl(MethodImplOptions.AggressiveInlining)] NarrowFourUtf16CharsToAsciiAndWriteToBuffer(ref byte outputBuffer, ulong value)264*1b3f573fSAndroid Build Coastguard Worker private static void NarrowFourUtf16CharsToAsciiAndWriteToBuffer(ref byte outputBuffer, ulong value) 265*1b3f573fSAndroid Build Coastguard Worker { 266*1b3f573fSAndroid Build Coastguard Worker #if GOOGLE_PROTOBUF_SIMD 267*1b3f573fSAndroid Build Coastguard Worker if (Sse2.X64.IsSupported) 268*1b3f573fSAndroid Build Coastguard Worker { 269*1b3f573fSAndroid Build Coastguard Worker // Narrows a vector of words [ w0 w1 w2 w3 ] to a vector of bytes 270*1b3f573fSAndroid Build Coastguard Worker // [ b0 b1 b2 b3 b0 b1 b2 b3 ], then writes 4 bytes (32 bits) to the destination. 271*1b3f573fSAndroid Build Coastguard Worker 272*1b3f573fSAndroid Build Coastguard Worker Vector128<short> vecWide = Sse2.X64.ConvertScalarToVector128UInt64(value).AsInt16(); 273*1b3f573fSAndroid Build Coastguard Worker Vector128<uint> vecNarrow = Sse2.PackUnsignedSaturate(vecWide, vecWide).AsUInt32(); 274*1b3f573fSAndroid Build Coastguard Worker Unsafe.WriteUnaligned<uint>(ref outputBuffer, Sse2.ConvertToUInt32(vecNarrow)); 275*1b3f573fSAndroid Build Coastguard Worker } 276*1b3f573fSAndroid Build Coastguard Worker else if (AdvSimd.IsSupported) 277*1b3f573fSAndroid Build Coastguard Worker { 278*1b3f573fSAndroid Build Coastguard Worker // Narrows a vector of words [ w0 w1 w2 w3 ] to a vector of bytes 279*1b3f573fSAndroid Build Coastguard Worker // [ b0 b1 b2 b3 * * * * ], then writes 4 bytes (32 bits) to the destination. 280*1b3f573fSAndroid Build Coastguard Worker 281*1b3f573fSAndroid Build Coastguard Worker Vector128<short> vecWide = Vector128.CreateScalarUnsafe(value).AsInt16(); 282*1b3f573fSAndroid Build Coastguard Worker Vector64<byte> lower = AdvSimd.ExtractNarrowingSaturateUnsignedLower(vecWide); 283*1b3f573fSAndroid Build Coastguard Worker Unsafe.WriteUnaligned<uint>(ref outputBuffer, lower.AsUInt32().ToScalar()); 284*1b3f573fSAndroid Build Coastguard Worker } 285*1b3f573fSAndroid Build Coastguard Worker else 286*1b3f573fSAndroid Build Coastguard Worker #endif 287*1b3f573fSAndroid Build Coastguard Worker { 288*1b3f573fSAndroid Build Coastguard Worker // Fallback to non-SIMD approach when SIMD is not available. 289*1b3f573fSAndroid Build Coastguard Worker // This could happen either because the APIs are not available, or hardware doesn't support it. 290*1b3f573fSAndroid Build Coastguard Worker // Processing 4 chars at a time in this fallback is still faster than casting one char at a time. 291*1b3f573fSAndroid Build Coastguard Worker if (BitConverter.IsLittleEndian) 292*1b3f573fSAndroid Build Coastguard Worker { 293*1b3f573fSAndroid Build Coastguard Worker outputBuffer = (byte)value; 294*1b3f573fSAndroid Build Coastguard Worker value >>= 16; 295*1b3f573fSAndroid Build Coastguard Worker Unsafe.Add(ref outputBuffer, 1) = (byte)value; 296*1b3f573fSAndroid Build Coastguard Worker value >>= 16; 297*1b3f573fSAndroid Build Coastguard Worker Unsafe.Add(ref outputBuffer, 2) = (byte)value; 298*1b3f573fSAndroid Build Coastguard Worker value >>= 16; 299*1b3f573fSAndroid Build Coastguard Worker Unsafe.Add(ref outputBuffer, 3) = (byte)value; 300*1b3f573fSAndroid Build Coastguard Worker } 301*1b3f573fSAndroid Build Coastguard Worker else 302*1b3f573fSAndroid Build Coastguard Worker { 303*1b3f573fSAndroid Build Coastguard Worker Unsafe.Add(ref outputBuffer, 3) = (byte)value; 304*1b3f573fSAndroid Build Coastguard Worker value >>= 16; 305*1b3f573fSAndroid Build Coastguard Worker Unsafe.Add(ref outputBuffer, 2) = (byte)value; 306*1b3f573fSAndroid Build Coastguard Worker value >>= 16; 307*1b3f573fSAndroid Build Coastguard Worker Unsafe.Add(ref outputBuffer, 1) = (byte)value; 308*1b3f573fSAndroid Build Coastguard Worker value >>= 16; 309*1b3f573fSAndroid Build Coastguard Worker outputBuffer = (byte)value; 310*1b3f573fSAndroid Build Coastguard Worker } 311*1b3f573fSAndroid Build Coastguard Worker } 312*1b3f573fSAndroid Build Coastguard Worker } 313*1b3f573fSAndroid Build Coastguard Worker WriteStringToBuffer(Span<byte> buffer, ref WriterInternalState state, string value)314*1b3f573fSAndroid Build Coastguard Worker private static int WriteStringToBuffer(Span<byte> buffer, ref WriterInternalState state, string value) 315*1b3f573fSAndroid Build Coastguard Worker { 316*1b3f573fSAndroid Build Coastguard Worker #if NETSTANDARD1_1 317*1b3f573fSAndroid Build Coastguard Worker // slowpath when Encoding.GetBytes(Char*, Int32, Byte*, Int32) is not available 318*1b3f573fSAndroid Build Coastguard Worker byte[] bytes = Utf8Encoding.GetBytes(value); 319*1b3f573fSAndroid Build Coastguard Worker WriteRawBytes(ref buffer, ref state, bytes); 320*1b3f573fSAndroid Build Coastguard Worker return bytes.Length; 321*1b3f573fSAndroid Build Coastguard Worker #else 322*1b3f573fSAndroid Build Coastguard Worker ReadOnlySpan<char> source = value.AsSpan(); 323*1b3f573fSAndroid Build Coastguard Worker int bytesUsed; 324*1b3f573fSAndroid Build Coastguard Worker unsafe 325*1b3f573fSAndroid Build Coastguard Worker { 326*1b3f573fSAndroid Build Coastguard Worker fixed (char* sourceChars = &MemoryMarshal.GetReference(source)) 327*1b3f573fSAndroid Build Coastguard Worker fixed (byte* destinationBytes = &MemoryMarshal.GetReference(buffer)) 328*1b3f573fSAndroid Build Coastguard Worker { 329*1b3f573fSAndroid Build Coastguard Worker bytesUsed = Utf8Encoding.GetBytes( 330*1b3f573fSAndroid Build Coastguard Worker sourceChars, 331*1b3f573fSAndroid Build Coastguard Worker source.Length, 332*1b3f573fSAndroid Build Coastguard Worker destinationBytes + state.position, 333*1b3f573fSAndroid Build Coastguard Worker buffer.Length - state.position); 334*1b3f573fSAndroid Build Coastguard Worker } 335*1b3f573fSAndroid Build Coastguard Worker } 336*1b3f573fSAndroid Build Coastguard Worker state.position += bytesUsed; 337*1b3f573fSAndroid Build Coastguard Worker return bytesUsed; 338*1b3f573fSAndroid Build Coastguard Worker #endif 339*1b3f573fSAndroid Build Coastguard Worker } 340*1b3f573fSAndroid Build Coastguard Worker 341*1b3f573fSAndroid Build Coastguard Worker /// <summary> 342*1b3f573fSAndroid Build Coastguard Worker /// Write a byte string, without a tag, to the stream. 343*1b3f573fSAndroid Build Coastguard Worker /// The data is length-prefixed. 344*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteBytes(ref Span<byte> buffer, ref WriterInternalState state, ByteString value)345*1b3f573fSAndroid Build Coastguard Worker public static void WriteBytes(ref Span<byte> buffer, ref WriterInternalState state, ByteString value) 346*1b3f573fSAndroid Build Coastguard Worker { 347*1b3f573fSAndroid Build Coastguard Worker WriteLength(ref buffer, ref state, value.Length); 348*1b3f573fSAndroid Build Coastguard Worker WriteRawBytes(ref buffer, ref state, value.Span); 349*1b3f573fSAndroid Build Coastguard Worker } 350*1b3f573fSAndroid Build Coastguard Worker 351*1b3f573fSAndroid Build Coastguard Worker /// <summary> 352*1b3f573fSAndroid Build Coastguard Worker /// Writes a uint32 value, without a tag, to the stream. 353*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteUInt32(ref Span<byte> buffer, ref WriterInternalState state, uint value)354*1b3f573fSAndroid Build Coastguard Worker public static void WriteUInt32(ref Span<byte> buffer, ref WriterInternalState state, uint value) 355*1b3f573fSAndroid Build Coastguard Worker { 356*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint32(ref buffer, ref state, value); 357*1b3f573fSAndroid Build Coastguard Worker } 358*1b3f573fSAndroid Build Coastguard Worker 359*1b3f573fSAndroid Build Coastguard Worker /// <summary> 360*1b3f573fSAndroid Build Coastguard Worker /// Writes an enum value, without a tag, to the stream. 361*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteEnum(ref Span<byte> buffer, ref WriterInternalState state, int value)362*1b3f573fSAndroid Build Coastguard Worker public static void WriteEnum(ref Span<byte> buffer, ref WriterInternalState state, int value) 363*1b3f573fSAndroid Build Coastguard Worker { 364*1b3f573fSAndroid Build Coastguard Worker WriteInt32(ref buffer, ref state, value); 365*1b3f573fSAndroid Build Coastguard Worker } 366*1b3f573fSAndroid Build Coastguard Worker 367*1b3f573fSAndroid Build Coastguard Worker /// <summary> 368*1b3f573fSAndroid Build Coastguard Worker /// Writes an sfixed32 value, without a tag, to the stream. 369*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteSFixed32(ref Span<byte> buffer, ref WriterInternalState state, int value)370*1b3f573fSAndroid Build Coastguard Worker public static void WriteSFixed32(ref Span<byte> buffer, ref WriterInternalState state, int value) 371*1b3f573fSAndroid Build Coastguard Worker { 372*1b3f573fSAndroid Build Coastguard Worker WriteRawLittleEndian32(ref buffer, ref state, (uint)value); 373*1b3f573fSAndroid Build Coastguard Worker } 374*1b3f573fSAndroid Build Coastguard Worker 375*1b3f573fSAndroid Build Coastguard Worker /// <summary> 376*1b3f573fSAndroid Build Coastguard Worker /// Writes an sfixed64 value, without a tag, to the stream. 377*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteSFixed64(ref Span<byte> buffer, ref WriterInternalState state, long value)378*1b3f573fSAndroid Build Coastguard Worker public static void WriteSFixed64(ref Span<byte> buffer, ref WriterInternalState state, long value) 379*1b3f573fSAndroid Build Coastguard Worker { 380*1b3f573fSAndroid Build Coastguard Worker WriteRawLittleEndian64(ref buffer, ref state, (ulong)value); 381*1b3f573fSAndroid Build Coastguard Worker } 382*1b3f573fSAndroid Build Coastguard Worker 383*1b3f573fSAndroid Build Coastguard Worker /// <summary> 384*1b3f573fSAndroid Build Coastguard Worker /// Writes an sint32 value, without a tag, to the stream. 385*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteSInt32(ref Span<byte> buffer, ref WriterInternalState state, int value)386*1b3f573fSAndroid Build Coastguard Worker public static void WriteSInt32(ref Span<byte> buffer, ref WriterInternalState state, int value) 387*1b3f573fSAndroid Build Coastguard Worker { 388*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint32(ref buffer, ref state, EncodeZigZag32(value)); 389*1b3f573fSAndroid Build Coastguard Worker } 390*1b3f573fSAndroid Build Coastguard Worker 391*1b3f573fSAndroid Build Coastguard Worker /// <summary> 392*1b3f573fSAndroid Build Coastguard Worker /// Writes an sint64 value, without a tag, to the stream. 393*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteSInt64(ref Span<byte> buffer, ref WriterInternalState state, long value)394*1b3f573fSAndroid Build Coastguard Worker public static void WriteSInt64(ref Span<byte> buffer, ref WriterInternalState state, long value) 395*1b3f573fSAndroid Build Coastguard Worker { 396*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint64(ref buffer, ref state, EncodeZigZag64(value)); 397*1b3f573fSAndroid Build Coastguard Worker } 398*1b3f573fSAndroid Build Coastguard Worker 399*1b3f573fSAndroid Build Coastguard Worker /// <summary> 400*1b3f573fSAndroid Build Coastguard Worker /// Writes a length (in bytes) for length-delimited data. 401*1b3f573fSAndroid Build Coastguard Worker /// </summary> 402*1b3f573fSAndroid Build Coastguard Worker /// <remarks> 403*1b3f573fSAndroid Build Coastguard Worker /// This method simply writes a rawint, but exists for clarity in calling code. 404*1b3f573fSAndroid Build Coastguard Worker /// </remarks> WriteLength(ref Span<byte> buffer, ref WriterInternalState state, int length)405*1b3f573fSAndroid Build Coastguard Worker public static void WriteLength(ref Span<byte> buffer, ref WriterInternalState state, int length) 406*1b3f573fSAndroid Build Coastguard Worker { 407*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint32(ref buffer, ref state, (uint)length); 408*1b3f573fSAndroid Build Coastguard Worker } 409*1b3f573fSAndroid Build Coastguard Worker 410*1b3f573fSAndroid Build Coastguard Worker #endregion 411*1b3f573fSAndroid Build Coastguard Worker 412*1b3f573fSAndroid Build Coastguard Worker #region Writing primitives 413*1b3f573fSAndroid Build Coastguard Worker /// <summary> 414*1b3f573fSAndroid Build Coastguard Worker /// Writes a 32 bit value as a varint. The fast route is taken when 415*1b3f573fSAndroid Build Coastguard Worker /// there's enough buffer space left to whizz through without checking 416*1b3f573fSAndroid Build Coastguard Worker /// for each byte; otherwise, we resort to calling WriteRawByte each time. 417*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteRawVarint32(ref Span<byte> buffer, ref WriterInternalState state, uint value)418*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawVarint32(ref Span<byte> buffer, ref WriterInternalState state, uint value) 419*1b3f573fSAndroid Build Coastguard Worker { 420*1b3f573fSAndroid Build Coastguard Worker // Optimize for the common case of a single byte value 421*1b3f573fSAndroid Build Coastguard Worker if (value < 128 && state.position < buffer.Length) 422*1b3f573fSAndroid Build Coastguard Worker { 423*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = (byte)value; 424*1b3f573fSAndroid Build Coastguard Worker return; 425*1b3f573fSAndroid Build Coastguard Worker } 426*1b3f573fSAndroid Build Coastguard Worker 427*1b3f573fSAndroid Build Coastguard Worker // Fast path when capacity is available 428*1b3f573fSAndroid Build Coastguard Worker while (state.position < buffer.Length) 429*1b3f573fSAndroid Build Coastguard Worker { 430*1b3f573fSAndroid Build Coastguard Worker if (value > 127) 431*1b3f573fSAndroid Build Coastguard Worker { 432*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = (byte)((value & 0x7F) | 0x80); 433*1b3f573fSAndroid Build Coastguard Worker value >>= 7; 434*1b3f573fSAndroid Build Coastguard Worker } 435*1b3f573fSAndroid Build Coastguard Worker else 436*1b3f573fSAndroid Build Coastguard Worker { 437*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = (byte)value; 438*1b3f573fSAndroid Build Coastguard Worker return; 439*1b3f573fSAndroid Build Coastguard Worker } 440*1b3f573fSAndroid Build Coastguard Worker } 441*1b3f573fSAndroid Build Coastguard Worker 442*1b3f573fSAndroid Build Coastguard Worker while (value > 127) 443*1b3f573fSAndroid Build Coastguard Worker { 444*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)((value & 0x7F) | 0x80)); 445*1b3f573fSAndroid Build Coastguard Worker value >>= 7; 446*1b3f573fSAndroid Build Coastguard Worker } 447*1b3f573fSAndroid Build Coastguard Worker 448*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)value); 449*1b3f573fSAndroid Build Coastguard Worker } 450*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)451*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawVarint64(ref Span<byte> buffer, ref WriterInternalState state, ulong value) 452*1b3f573fSAndroid Build Coastguard Worker { 453*1b3f573fSAndroid Build Coastguard Worker // Optimize for the common case of a single byte value 454*1b3f573fSAndroid Build Coastguard Worker if (value < 128 && state.position < buffer.Length) 455*1b3f573fSAndroid Build Coastguard Worker { 456*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = (byte)value; 457*1b3f573fSAndroid Build Coastguard Worker return; 458*1b3f573fSAndroid Build Coastguard Worker } 459*1b3f573fSAndroid Build Coastguard Worker 460*1b3f573fSAndroid Build Coastguard Worker // Fast path when capacity is available 461*1b3f573fSAndroid Build Coastguard Worker while (state.position < buffer.Length) 462*1b3f573fSAndroid Build Coastguard Worker { 463*1b3f573fSAndroid Build Coastguard Worker if (value > 127) 464*1b3f573fSAndroid Build Coastguard Worker { 465*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = (byte)((value & 0x7F) | 0x80); 466*1b3f573fSAndroid Build Coastguard Worker value >>= 7; 467*1b3f573fSAndroid Build Coastguard Worker } 468*1b3f573fSAndroid Build Coastguard Worker else 469*1b3f573fSAndroid Build Coastguard Worker { 470*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = (byte)value; 471*1b3f573fSAndroid Build Coastguard Worker return; 472*1b3f573fSAndroid Build Coastguard Worker } 473*1b3f573fSAndroid Build Coastguard Worker } 474*1b3f573fSAndroid Build Coastguard Worker 475*1b3f573fSAndroid Build Coastguard Worker while (value > 127) 476*1b3f573fSAndroid Build Coastguard Worker { 477*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)((value & 0x7F) | 0x80)); 478*1b3f573fSAndroid Build Coastguard Worker value >>= 7; 479*1b3f573fSAndroid Build Coastguard Worker } 480*1b3f573fSAndroid Build Coastguard Worker 481*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)value); 482*1b3f573fSAndroid Build Coastguard Worker } 483*1b3f573fSAndroid Build Coastguard Worker WriteRawLittleEndian32(ref Span<byte> buffer, ref WriterInternalState state, uint value)484*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawLittleEndian32(ref Span<byte> buffer, ref WriterInternalState state, uint value) 485*1b3f573fSAndroid Build Coastguard Worker { 486*1b3f573fSAndroid Build Coastguard Worker const int length = sizeof(uint); 487*1b3f573fSAndroid Build Coastguard Worker if (state.position + length > buffer.Length) 488*1b3f573fSAndroid Build Coastguard Worker { 489*1b3f573fSAndroid Build Coastguard Worker WriteRawLittleEndian32SlowPath(ref buffer, ref state, value); 490*1b3f573fSAndroid Build Coastguard Worker } 491*1b3f573fSAndroid Build Coastguard Worker else 492*1b3f573fSAndroid Build Coastguard Worker { 493*1b3f573fSAndroid Build Coastguard Worker BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(state.position), value); 494*1b3f573fSAndroid Build Coastguard Worker state.position += length; 495*1b3f573fSAndroid Build Coastguard Worker } 496*1b3f573fSAndroid Build Coastguard Worker } 497*1b3f573fSAndroid Build Coastguard Worker 498*1b3f573fSAndroid Build Coastguard Worker [MethodImpl(MethodImplOptions.NoInlining)] WriteRawLittleEndian32SlowPath(ref Span<byte> buffer, ref WriterInternalState state, uint value)499*1b3f573fSAndroid Build Coastguard Worker private static void WriteRawLittleEndian32SlowPath(ref Span<byte> buffer, ref WriterInternalState state, uint value) 500*1b3f573fSAndroid Build Coastguard Worker { 501*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)value); 502*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)(value >> 8)); 503*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)(value >> 16)); 504*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)(value >> 24)); 505*1b3f573fSAndroid Build Coastguard Worker } 506*1b3f573fSAndroid Build Coastguard Worker WriteRawLittleEndian64(ref Span<byte> buffer, ref WriterInternalState state, ulong value)507*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawLittleEndian64(ref Span<byte> buffer, ref WriterInternalState state, ulong value) 508*1b3f573fSAndroid Build Coastguard Worker { 509*1b3f573fSAndroid Build Coastguard Worker const int length = sizeof(ulong); 510*1b3f573fSAndroid Build Coastguard Worker if (state.position + length > buffer.Length) 511*1b3f573fSAndroid Build Coastguard Worker { 512*1b3f573fSAndroid Build Coastguard Worker WriteRawLittleEndian64SlowPath(ref buffer, ref state, value); 513*1b3f573fSAndroid Build Coastguard Worker } 514*1b3f573fSAndroid Build Coastguard Worker else 515*1b3f573fSAndroid Build Coastguard Worker { 516*1b3f573fSAndroid Build Coastguard Worker BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(state.position), value); 517*1b3f573fSAndroid Build Coastguard Worker state.position += length; 518*1b3f573fSAndroid Build Coastguard Worker } 519*1b3f573fSAndroid Build Coastguard Worker } 520*1b3f573fSAndroid Build Coastguard Worker 521*1b3f573fSAndroid Build Coastguard Worker [MethodImpl(MethodImplOptions.NoInlining)] WriteRawLittleEndian64SlowPath(ref Span<byte> buffer, ref WriterInternalState state, ulong value)522*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawLittleEndian64SlowPath(ref Span<byte> buffer, ref WriterInternalState state, ulong value) 523*1b3f573fSAndroid Build Coastguard Worker { 524*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)value); 525*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)(value >> 8)); 526*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)(value >> 16)); 527*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)(value >> 24)); 528*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)(value >> 32)); 529*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)(value >> 40)); 530*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)(value >> 48)); 531*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, (byte)(value >> 56)); 532*1b3f573fSAndroid Build Coastguard Worker } 533*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref Span<byte> buffer, ref WriterInternalState state, byte value)534*1b3f573fSAndroid Build Coastguard Worker private static void WriteRawByte(ref Span<byte> buffer, ref WriterInternalState state, byte value) 535*1b3f573fSAndroid Build Coastguard Worker { 536*1b3f573fSAndroid Build Coastguard Worker if (state.position == buffer.Length) 537*1b3f573fSAndroid Build Coastguard Worker { 538*1b3f573fSAndroid Build Coastguard Worker WriteBufferHelper.RefreshBuffer(ref buffer, ref state); 539*1b3f573fSAndroid Build Coastguard Worker } 540*1b3f573fSAndroid Build Coastguard Worker 541*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = value; 542*1b3f573fSAndroid Build Coastguard Worker } 543*1b3f573fSAndroid Build Coastguard Worker 544*1b3f573fSAndroid Build Coastguard Worker /// <summary> 545*1b3f573fSAndroid Build Coastguard Worker /// Writes out an array of bytes. 546*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, byte[] value)547*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, byte[] value) 548*1b3f573fSAndroid Build Coastguard Worker { 549*1b3f573fSAndroid Build Coastguard Worker WriteRawBytes(ref buffer, ref state, new ReadOnlySpan<byte>(value)); 550*1b3f573fSAndroid Build Coastguard Worker } 551*1b3f573fSAndroid Build Coastguard Worker 552*1b3f573fSAndroid Build Coastguard Worker /// <summary> 553*1b3f573fSAndroid Build Coastguard Worker /// Writes out part of an array of bytes. 554*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, byte[] value, int offset, int length)555*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, byte[] value, int offset, int length) 556*1b3f573fSAndroid Build Coastguard Worker { 557*1b3f573fSAndroid Build Coastguard Worker WriteRawBytes(ref buffer, ref state, new ReadOnlySpan<byte>(value, offset, length)); 558*1b3f573fSAndroid Build Coastguard Worker } 559*1b3f573fSAndroid Build Coastguard Worker 560*1b3f573fSAndroid Build Coastguard Worker /// <summary> 561*1b3f573fSAndroid Build Coastguard Worker /// Writes out part of an array of bytes. 562*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, ReadOnlySpan<byte> value)563*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawBytes(ref Span<byte> buffer, ref WriterInternalState state, ReadOnlySpan<byte> value) 564*1b3f573fSAndroid Build Coastguard Worker { 565*1b3f573fSAndroid Build Coastguard Worker if (buffer.Length - state.position >= value.Length) 566*1b3f573fSAndroid Build Coastguard Worker { 567*1b3f573fSAndroid Build Coastguard Worker // We have room in the current buffer. 568*1b3f573fSAndroid Build Coastguard Worker value.CopyTo(buffer.Slice(state.position, value.Length)); 569*1b3f573fSAndroid Build Coastguard Worker state.position += value.Length; 570*1b3f573fSAndroid Build Coastguard Worker } 571*1b3f573fSAndroid Build Coastguard Worker else 572*1b3f573fSAndroid Build Coastguard Worker { 573*1b3f573fSAndroid Build Coastguard Worker // When writing to a CodedOutputStream backed by a Stream, we could avoid 574*1b3f573fSAndroid Build Coastguard Worker // copying the data twice (first copying to the current buffer and 575*1b3f573fSAndroid Build Coastguard Worker // and later writing from the current buffer to the underlying Stream) 576*1b3f573fSAndroid Build Coastguard Worker // in some circumstances by writing the data directly to the underlying Stream. 577*1b3f573fSAndroid Build Coastguard Worker // Current this is not being done to avoid specialcasing the code for 578*1b3f573fSAndroid Build Coastguard Worker // CodedOutputStream vs IBufferWriter<byte>. 579*1b3f573fSAndroid Build Coastguard Worker int bytesWritten = 0; 580*1b3f573fSAndroid Build Coastguard Worker while (buffer.Length - state.position < value.Length - bytesWritten) 581*1b3f573fSAndroid Build Coastguard Worker { 582*1b3f573fSAndroid Build Coastguard Worker int length = buffer.Length - state.position; 583*1b3f573fSAndroid Build Coastguard Worker value.Slice(bytesWritten, length).CopyTo(buffer.Slice(state.position, length)); 584*1b3f573fSAndroid Build Coastguard Worker bytesWritten += length; 585*1b3f573fSAndroid Build Coastguard Worker state.position += length; 586*1b3f573fSAndroid Build Coastguard Worker WriteBufferHelper.RefreshBuffer(ref buffer, ref state); 587*1b3f573fSAndroid Build Coastguard Worker } 588*1b3f573fSAndroid Build Coastguard Worker 589*1b3f573fSAndroid Build Coastguard Worker // copy the remaining data 590*1b3f573fSAndroid Build Coastguard Worker int remainderLength = value.Length - bytesWritten; 591*1b3f573fSAndroid Build Coastguard Worker value.Slice(bytesWritten, remainderLength).CopyTo(buffer.Slice(state.position, remainderLength)); 592*1b3f573fSAndroid Build Coastguard Worker state.position += remainderLength; 593*1b3f573fSAndroid Build Coastguard Worker } 594*1b3f573fSAndroid Build Coastguard Worker } 595*1b3f573fSAndroid Build Coastguard Worker #endregion 596*1b3f573fSAndroid Build Coastguard Worker 597*1b3f573fSAndroid Build Coastguard Worker #region Raw tag writing 598*1b3f573fSAndroid Build Coastguard Worker /// <summary> 599*1b3f573fSAndroid Build Coastguard Worker /// Encodes and writes a tag. 600*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteTag(ref Span<byte> buffer, ref WriterInternalState state, int fieldNumber, WireFormat.WireType type)601*1b3f573fSAndroid Build Coastguard Worker public static void WriteTag(ref Span<byte> buffer, ref WriterInternalState state, int fieldNumber, WireFormat.WireType type) 602*1b3f573fSAndroid Build Coastguard Worker { 603*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint32(ref buffer, ref state, WireFormat.MakeTag(fieldNumber, type)); 604*1b3f573fSAndroid Build Coastguard Worker } 605*1b3f573fSAndroid Build Coastguard Worker 606*1b3f573fSAndroid Build Coastguard Worker /// <summary> 607*1b3f573fSAndroid Build Coastguard Worker /// Writes an already-encoded tag. 608*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteTag(ref Span<byte> buffer, ref WriterInternalState state, uint tag)609*1b3f573fSAndroid Build Coastguard Worker public static void WriteTag(ref Span<byte> buffer, ref WriterInternalState state, uint tag) 610*1b3f573fSAndroid Build Coastguard Worker { 611*1b3f573fSAndroid Build Coastguard Worker WriteRawVarint32(ref buffer, ref state, tag); 612*1b3f573fSAndroid Build Coastguard Worker } 613*1b3f573fSAndroid Build Coastguard Worker 614*1b3f573fSAndroid Build Coastguard Worker /// <summary> 615*1b3f573fSAndroid Build Coastguard Worker /// Writes the given single-byte tag directly to the stream. 616*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1)617*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1) 618*1b3f573fSAndroid Build Coastguard Worker { 619*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b1); 620*1b3f573fSAndroid Build Coastguard Worker } 621*1b3f573fSAndroid Build Coastguard Worker 622*1b3f573fSAndroid Build Coastguard Worker /// <summary> 623*1b3f573fSAndroid Build Coastguard Worker /// Writes the given two-byte tag directly to the stream. 624*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2)625*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2) 626*1b3f573fSAndroid Build Coastguard Worker { 627*1b3f573fSAndroid Build Coastguard Worker if (state.position + 2 > buffer.Length) 628*1b3f573fSAndroid Build Coastguard Worker { 629*1b3f573fSAndroid Build Coastguard Worker WriteRawTagSlowPath(ref buffer, ref state, b1, b2); 630*1b3f573fSAndroid Build Coastguard Worker } 631*1b3f573fSAndroid Build Coastguard Worker else 632*1b3f573fSAndroid Build Coastguard Worker { 633*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b1; 634*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b2; 635*1b3f573fSAndroid Build Coastguard Worker } 636*1b3f573fSAndroid Build Coastguard Worker } 637*1b3f573fSAndroid Build Coastguard Worker 638*1b3f573fSAndroid Build Coastguard Worker [MethodImpl(MethodImplOptions.NoInlining)] WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2)639*1b3f573fSAndroid Build Coastguard Worker private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2) 640*1b3f573fSAndroid Build Coastguard Worker { 641*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b1); 642*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b2); 643*1b3f573fSAndroid Build Coastguard Worker } 644*1b3f573fSAndroid Build Coastguard Worker 645*1b3f573fSAndroid Build Coastguard Worker /// <summary> 646*1b3f573fSAndroid Build Coastguard Worker /// Writes the given three-byte tag directly to the stream. 647*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3)648*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3) 649*1b3f573fSAndroid Build Coastguard Worker { 650*1b3f573fSAndroid Build Coastguard Worker if (state.position + 3 > buffer.Length) 651*1b3f573fSAndroid Build Coastguard Worker { 652*1b3f573fSAndroid Build Coastguard Worker WriteRawTagSlowPath(ref buffer, ref state, b1, b2, b3); 653*1b3f573fSAndroid Build Coastguard Worker } 654*1b3f573fSAndroid Build Coastguard Worker else 655*1b3f573fSAndroid Build Coastguard Worker { 656*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b1; 657*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b2; 658*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b3; 659*1b3f573fSAndroid Build Coastguard Worker } 660*1b3f573fSAndroid Build Coastguard Worker } 661*1b3f573fSAndroid Build Coastguard Worker 662*1b3f573fSAndroid Build Coastguard Worker [MethodImpl(MethodImplOptions.NoInlining)] WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3)663*1b3f573fSAndroid Build Coastguard Worker private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3) 664*1b3f573fSAndroid Build Coastguard Worker { 665*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b1); 666*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b2); 667*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b3); 668*1b3f573fSAndroid Build Coastguard Worker } 669*1b3f573fSAndroid Build Coastguard Worker 670*1b3f573fSAndroid Build Coastguard Worker /// <summary> 671*1b3f573fSAndroid Build Coastguard Worker /// Writes the given four-byte tag directly to the stream. 672*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4)673*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4) 674*1b3f573fSAndroid Build Coastguard Worker { 675*1b3f573fSAndroid Build Coastguard Worker if (state.position + 4 > buffer.Length) 676*1b3f573fSAndroid Build Coastguard Worker { 677*1b3f573fSAndroid Build Coastguard Worker WriteRawTagSlowPath(ref buffer, ref state, b1, b2, b3, b4); 678*1b3f573fSAndroid Build Coastguard Worker } 679*1b3f573fSAndroid Build Coastguard Worker else 680*1b3f573fSAndroid Build Coastguard Worker { 681*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b1; 682*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b2; 683*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b3; 684*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b4; 685*1b3f573fSAndroid Build Coastguard Worker } 686*1b3f573fSAndroid Build Coastguard Worker } 687*1b3f573fSAndroid Build Coastguard Worker 688*1b3f573fSAndroid Build Coastguard Worker [MethodImpl(MethodImplOptions.NoInlining)] 689*1b3f573fSAndroid Build Coastguard Worker WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4)690*1b3f573fSAndroid Build Coastguard Worker private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4) 691*1b3f573fSAndroid Build Coastguard Worker { 692*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b1); 693*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b2); 694*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b3); 695*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b4); 696*1b3f573fSAndroid Build Coastguard Worker } 697*1b3f573fSAndroid Build Coastguard Worker 698*1b3f573fSAndroid Build Coastguard Worker /// <summary> 699*1b3f573fSAndroid Build Coastguard Worker /// Writes the given five-byte tag directly to the stream. 700*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4, byte b5)701*1b3f573fSAndroid Build Coastguard Worker public static void WriteRawTag(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4, byte b5) 702*1b3f573fSAndroid Build Coastguard Worker { 703*1b3f573fSAndroid Build Coastguard Worker if (state.position + 5 > buffer.Length) 704*1b3f573fSAndroid Build Coastguard Worker { 705*1b3f573fSAndroid Build Coastguard Worker WriteRawTagSlowPath(ref buffer, ref state, b1, b2, b3, b4, b5); 706*1b3f573fSAndroid Build Coastguard Worker } 707*1b3f573fSAndroid Build Coastguard Worker else 708*1b3f573fSAndroid Build Coastguard Worker { 709*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b1; 710*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b2; 711*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b3; 712*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b4; 713*1b3f573fSAndroid Build Coastguard Worker buffer[state.position++] = b5; 714*1b3f573fSAndroid Build Coastguard Worker } 715*1b3f573fSAndroid Build Coastguard Worker } 716*1b3f573fSAndroid Build Coastguard Worker 717*1b3f573fSAndroid Build Coastguard Worker [MethodImpl(MethodImplOptions.NoInlining)] WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4, byte b5)718*1b3f573fSAndroid Build Coastguard Worker private static void WriteRawTagSlowPath(ref Span<byte> buffer, ref WriterInternalState state, byte b1, byte b2, byte b3, byte b4, byte b5) 719*1b3f573fSAndroid Build Coastguard Worker { 720*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b1); 721*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b2); 722*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b3); 723*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b4); 724*1b3f573fSAndroid Build Coastguard Worker WriteRawByte(ref buffer, ref state, b5); 725*1b3f573fSAndroid Build Coastguard Worker } 726*1b3f573fSAndroid Build Coastguard Worker #endregion 727*1b3f573fSAndroid Build Coastguard Worker 728*1b3f573fSAndroid Build Coastguard Worker /// <summary> 729*1b3f573fSAndroid Build Coastguard Worker /// Encode a 32-bit value with ZigZag encoding. 730*1b3f573fSAndroid Build Coastguard Worker /// </summary> 731*1b3f573fSAndroid Build Coastguard Worker /// <remarks> 732*1b3f573fSAndroid Build Coastguard Worker /// ZigZag encodes signed integers into values that can be efficiently 733*1b3f573fSAndroid Build Coastguard Worker /// encoded with varint. (Otherwise, negative values must be 734*1b3f573fSAndroid Build Coastguard Worker /// sign-extended to 64 bits to be varint encoded, thus always taking 735*1b3f573fSAndroid Build Coastguard Worker /// 10 bytes on the wire.) 736*1b3f573fSAndroid Build Coastguard Worker /// </remarks> EncodeZigZag32(int n)737*1b3f573fSAndroid Build Coastguard Worker public static uint EncodeZigZag32(int n) 738*1b3f573fSAndroid Build Coastguard Worker { 739*1b3f573fSAndroid Build Coastguard Worker // Note: the right-shift must be arithmetic 740*1b3f573fSAndroid Build Coastguard Worker return (uint)((n << 1) ^ (n >> 31)); 741*1b3f573fSAndroid Build Coastguard Worker } 742*1b3f573fSAndroid Build Coastguard Worker 743*1b3f573fSAndroid Build Coastguard Worker /// <summary> 744*1b3f573fSAndroid Build Coastguard Worker /// Encode a 64-bit value with ZigZag encoding. 745*1b3f573fSAndroid Build Coastguard Worker /// </summary> 746*1b3f573fSAndroid Build Coastguard Worker /// <remarks> 747*1b3f573fSAndroid Build Coastguard Worker /// ZigZag encodes signed integers into values that can be efficiently 748*1b3f573fSAndroid Build Coastguard Worker /// encoded with varint. (Otherwise, negative values must be 749*1b3f573fSAndroid Build Coastguard Worker /// sign-extended to 64 bits to be varint encoded, thus always taking 750*1b3f573fSAndroid Build Coastguard Worker /// 10 bytes on the wire.) 751*1b3f573fSAndroid Build Coastguard Worker /// </remarks> EncodeZigZag64(long n)752*1b3f573fSAndroid Build Coastguard Worker public static ulong EncodeZigZag64(long n) 753*1b3f573fSAndroid Build Coastguard Worker { 754*1b3f573fSAndroid Build Coastguard Worker return (ulong)((n << 1) ^ (n >> 63)); 755*1b3f573fSAndroid Build Coastguard Worker } 756*1b3f573fSAndroid Build Coastguard Worker } 757*1b3f573fSAndroid Build Coastguard Worker }