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