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