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