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.IO;
36ffe3c632Sopenharmony_ciusing System.Runtime.CompilerServices;
37ffe3c632Sopenharmony_ciusing System.Security;
38ffe3c632Sopenharmony_ci
39ffe3c632Sopenharmony_cinamespace Google.Protobuf
40ffe3c632Sopenharmony_ci{
41ffe3c632Sopenharmony_ci    /// <summary>
42ffe3c632Sopenharmony_ci    /// Abstraction for reading from a stream / read only sequence.
43ffe3c632Sopenharmony_ci    /// Parsing from the buffer is a loop of reading from current buffer / refreshing the buffer once done.
44ffe3c632Sopenharmony_ci    /// </summary>
45ffe3c632Sopenharmony_ci    [SecuritySafeCritical]
46ffe3c632Sopenharmony_ci    internal struct SegmentedBufferHelper
47ffe3c632Sopenharmony_ci    {
48ffe3c632Sopenharmony_ci        private int? totalLength;
49ffe3c632Sopenharmony_ci        private ReadOnlySequence<byte>.Enumerator readOnlySequenceEnumerator;
50ffe3c632Sopenharmony_ci        private CodedInputStream codedInputStream;
51ffe3c632Sopenharmony_ci
52ffe3c632Sopenharmony_ci        /// <summary>
53ffe3c632Sopenharmony_ci        /// Initialize an instance with a coded input stream.
54ffe3c632Sopenharmony_ci        /// This approach is faster than using a constructor because the instance to initialize is passed by reference
55ffe3c632Sopenharmony_ci        /// and we can write directly into it without copying.
56ffe3c632Sopenharmony_ci        /// </summary>
57ffe3c632Sopenharmony_ci        [MethodImpl(MethodImplOptions.AggressiveInlining)]
58ffe3c632Sopenharmony_ci        public static void Initialize(CodedInputStream codedInputStream, out SegmentedBufferHelper instance)
59ffe3c632Sopenharmony_ci        {
60ffe3c632Sopenharmony_ci            instance.totalLength = codedInputStream.InternalInputStream == null ? (int?)codedInputStream.InternalBuffer.Length : null;
61ffe3c632Sopenharmony_ci            instance.readOnlySequenceEnumerator = default;
62ffe3c632Sopenharmony_ci            instance.codedInputStream = codedInputStream;
63ffe3c632Sopenharmony_ci        }
64ffe3c632Sopenharmony_ci
65ffe3c632Sopenharmony_ci        /// <summary>
66ffe3c632Sopenharmony_ci        /// Initialize an instance with a read only sequence.
67ffe3c632Sopenharmony_ci        /// This approach is faster than using a constructor because the instance to initialize is passed by reference
68ffe3c632Sopenharmony_ci        /// and we can write directly into it without copying.
69ffe3c632Sopenharmony_ci        /// </summary>
70ffe3c632Sopenharmony_ci        [MethodImpl(MethodImplOptions.AggressiveInlining)]
71ffe3c632Sopenharmony_ci        public static void Initialize(ReadOnlySequence<byte> sequence, out SegmentedBufferHelper instance, out ReadOnlySpan<byte> firstSpan)
72ffe3c632Sopenharmony_ci        {
73ffe3c632Sopenharmony_ci            instance.codedInputStream = null;
74ffe3c632Sopenharmony_ci            if (sequence.IsSingleSegment)
75ffe3c632Sopenharmony_ci            {
76ffe3c632Sopenharmony_ci                firstSpan = sequence.First.Span;
77ffe3c632Sopenharmony_ci                instance.totalLength = firstSpan.Length;
78ffe3c632Sopenharmony_ci                instance.readOnlySequenceEnumerator = default;
79ffe3c632Sopenharmony_ci            }
80ffe3c632Sopenharmony_ci            else
81ffe3c632Sopenharmony_ci            {
82ffe3c632Sopenharmony_ci                instance.readOnlySequenceEnumerator = sequence.GetEnumerator();
83ffe3c632Sopenharmony_ci                instance.totalLength = (int) sequence.Length;
84ffe3c632Sopenharmony_ci
85ffe3c632Sopenharmony_ci                // set firstSpan to the first segment
86ffe3c632Sopenharmony_ci                instance.readOnlySequenceEnumerator.MoveNext();
87ffe3c632Sopenharmony_ci                firstSpan = instance.readOnlySequenceEnumerator.Current.Span;
88ffe3c632Sopenharmony_ci            }
89ffe3c632Sopenharmony_ci        }
90ffe3c632Sopenharmony_ci
91ffe3c632Sopenharmony_ci        public bool RefillBuffer(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, bool mustSucceed)
92ffe3c632Sopenharmony_ci        {
93ffe3c632Sopenharmony_ci            if (codedInputStream != null)
94ffe3c632Sopenharmony_ci            {
95ffe3c632Sopenharmony_ci                return RefillFromCodedInputStream(ref buffer, ref state, mustSucceed);
96ffe3c632Sopenharmony_ci            }
97ffe3c632Sopenharmony_ci            else
98ffe3c632Sopenharmony_ci            {
99ffe3c632Sopenharmony_ci                return RefillFromReadOnlySequence(ref buffer, ref state, mustSucceed);
100ffe3c632Sopenharmony_ci            }
101ffe3c632Sopenharmony_ci        }
102ffe3c632Sopenharmony_ci
103ffe3c632Sopenharmony_ci        public int? TotalLength => totalLength;
104ffe3c632Sopenharmony_ci
105ffe3c632Sopenharmony_ci        public CodedInputStream CodedInputStream => codedInputStream;
106ffe3c632Sopenharmony_ci
107ffe3c632Sopenharmony_ci        /// <summary>
108ffe3c632Sopenharmony_ci        /// Sets currentLimit to (current position) + byteLimit. This is called
109ffe3c632Sopenharmony_ci        /// when descending into a length-delimited embedded message. The previous
110ffe3c632Sopenharmony_ci        /// limit is returned.
111ffe3c632Sopenharmony_ci        /// </summary>
112ffe3c632Sopenharmony_ci        /// <returns>The old limit.</returns>
113ffe3c632Sopenharmony_ci        public static int PushLimit(ref ParserInternalState state, int byteLimit)
114ffe3c632Sopenharmony_ci        {
115ffe3c632Sopenharmony_ci            if (byteLimit < 0)
116ffe3c632Sopenharmony_ci            {
117ffe3c632Sopenharmony_ci                throw InvalidProtocolBufferException.NegativeSize();
118ffe3c632Sopenharmony_ci            }
119ffe3c632Sopenharmony_ci            byteLimit += state.totalBytesRetired + state.bufferPos;
120ffe3c632Sopenharmony_ci            int oldLimit = state.currentLimit;
121ffe3c632Sopenharmony_ci            if (byteLimit > oldLimit)
122ffe3c632Sopenharmony_ci            {
123ffe3c632Sopenharmony_ci                throw InvalidProtocolBufferException.TruncatedMessage();
124ffe3c632Sopenharmony_ci            }
125ffe3c632Sopenharmony_ci            state.currentLimit = byteLimit;
126ffe3c632Sopenharmony_ci
127ffe3c632Sopenharmony_ci            RecomputeBufferSizeAfterLimit(ref state);
128ffe3c632Sopenharmony_ci
129ffe3c632Sopenharmony_ci            return oldLimit;
130ffe3c632Sopenharmony_ci        }
131ffe3c632Sopenharmony_ci
132ffe3c632Sopenharmony_ci        /// <summary>
133ffe3c632Sopenharmony_ci        /// Discards the current limit, returning the previous limit.
134ffe3c632Sopenharmony_ci        /// </summary>
135ffe3c632Sopenharmony_ci        public static void PopLimit(ref ParserInternalState state, int oldLimit)
136ffe3c632Sopenharmony_ci        {
137ffe3c632Sopenharmony_ci            state.currentLimit = oldLimit;
138ffe3c632Sopenharmony_ci            RecomputeBufferSizeAfterLimit(ref state);
139ffe3c632Sopenharmony_ci        }
140ffe3c632Sopenharmony_ci
141ffe3c632Sopenharmony_ci        /// <summary>
142ffe3c632Sopenharmony_ci        /// Returns whether or not all the data before the limit has been read.
143ffe3c632Sopenharmony_ci        /// </summary>
144ffe3c632Sopenharmony_ci        /// <returns></returns>
145ffe3c632Sopenharmony_ci        public static bool IsReachedLimit(ref ParserInternalState state)
146ffe3c632Sopenharmony_ci        {
147ffe3c632Sopenharmony_ci            if (state.currentLimit == int.MaxValue)
148ffe3c632Sopenharmony_ci            {
149ffe3c632Sopenharmony_ci                return false;
150ffe3c632Sopenharmony_ci            }
151ffe3c632Sopenharmony_ci            int currentAbsolutePosition = state.totalBytesRetired + state.bufferPos;
152ffe3c632Sopenharmony_ci            return currentAbsolutePosition >= state.currentLimit;
153ffe3c632Sopenharmony_ci        }
154ffe3c632Sopenharmony_ci
155ffe3c632Sopenharmony_ci        /// <summary>
156ffe3c632Sopenharmony_ci        /// Returns true if the stream has reached the end of the input. This is the
157ffe3c632Sopenharmony_ci        /// case if either the end of the underlying input source has been reached or
158ffe3c632Sopenharmony_ci        /// the stream has reached a limit created using PushLimit.
159ffe3c632Sopenharmony_ci        /// </summary>
160ffe3c632Sopenharmony_ci        [MethodImpl(MethodImplOptions.AggressiveInlining)]
161ffe3c632Sopenharmony_ci        public static bool IsAtEnd(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)
162ffe3c632Sopenharmony_ci        {
163ffe3c632Sopenharmony_ci            return state.bufferPos == state.bufferSize && !state.segmentedBufferHelper.RefillBuffer(ref buffer, ref state, false);
164ffe3c632Sopenharmony_ci        }
165ffe3c632Sopenharmony_ci
166ffe3c632Sopenharmony_ci        private bool RefillFromReadOnlySequence(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, bool mustSucceed)
167ffe3c632Sopenharmony_ci        {
168ffe3c632Sopenharmony_ci            CheckCurrentBufferIsEmpty(ref state);
169ffe3c632Sopenharmony_ci
170ffe3c632Sopenharmony_ci            if (state.totalBytesRetired + state.bufferSize == state.currentLimit)
171ffe3c632Sopenharmony_ci            {
172ffe3c632Sopenharmony_ci                // Oops, we hit a limit.
173ffe3c632Sopenharmony_ci                if (mustSucceed)
174ffe3c632Sopenharmony_ci                {
175ffe3c632Sopenharmony_ci                    throw InvalidProtocolBufferException.TruncatedMessage();
176ffe3c632Sopenharmony_ci                }
177ffe3c632Sopenharmony_ci                else
178ffe3c632Sopenharmony_ci                {
179ffe3c632Sopenharmony_ci                    return false;
180ffe3c632Sopenharmony_ci                }
181ffe3c632Sopenharmony_ci            }
182ffe3c632Sopenharmony_ci
183ffe3c632Sopenharmony_ci            state.totalBytesRetired += state.bufferSize;
184ffe3c632Sopenharmony_ci
185ffe3c632Sopenharmony_ci            state.bufferPos = 0;
186ffe3c632Sopenharmony_ci            state.bufferSize = 0;
187ffe3c632Sopenharmony_ci            while (readOnlySequenceEnumerator.MoveNext())
188ffe3c632Sopenharmony_ci            {
189ffe3c632Sopenharmony_ci                buffer = readOnlySequenceEnumerator.Current.Span;
190ffe3c632Sopenharmony_ci                state.bufferSize = buffer.Length;
191ffe3c632Sopenharmony_ci                if (buffer.Length != 0)
192ffe3c632Sopenharmony_ci                {
193ffe3c632Sopenharmony_ci                    break;
194ffe3c632Sopenharmony_ci                }
195ffe3c632Sopenharmony_ci            }
196ffe3c632Sopenharmony_ci
197ffe3c632Sopenharmony_ci            if (state.bufferSize == 0)
198ffe3c632Sopenharmony_ci            {
199ffe3c632Sopenharmony_ci                if (mustSucceed)
200ffe3c632Sopenharmony_ci                {
201ffe3c632Sopenharmony_ci                    throw InvalidProtocolBufferException.TruncatedMessage();
202ffe3c632Sopenharmony_ci                }
203ffe3c632Sopenharmony_ci                else
204ffe3c632Sopenharmony_ci                {
205ffe3c632Sopenharmony_ci                    return false;
206ffe3c632Sopenharmony_ci                }
207ffe3c632Sopenharmony_ci            }
208ffe3c632Sopenharmony_ci            else
209ffe3c632Sopenharmony_ci            {
210ffe3c632Sopenharmony_ci                RecomputeBufferSizeAfterLimit(ref state);
211ffe3c632Sopenharmony_ci                int totalBytesRead =
212ffe3c632Sopenharmony_ci                    state.totalBytesRetired + state.bufferSize + state.bufferSizeAfterLimit;
213ffe3c632Sopenharmony_ci                if (totalBytesRead < 0 || totalBytesRead > state.sizeLimit)
214ffe3c632Sopenharmony_ci                {
215ffe3c632Sopenharmony_ci                    throw InvalidProtocolBufferException.SizeLimitExceeded();
216ffe3c632Sopenharmony_ci                }
217ffe3c632Sopenharmony_ci                return true;
218ffe3c632Sopenharmony_ci            }
219ffe3c632Sopenharmony_ci        }
220ffe3c632Sopenharmony_ci
221ffe3c632Sopenharmony_ci        private bool RefillFromCodedInputStream(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, bool mustSucceed)
222ffe3c632Sopenharmony_ci        {
223ffe3c632Sopenharmony_ci            CheckCurrentBufferIsEmpty(ref state);
224ffe3c632Sopenharmony_ci
225ffe3c632Sopenharmony_ci            if (state.totalBytesRetired + state.bufferSize == state.currentLimit)
226ffe3c632Sopenharmony_ci            {
227ffe3c632Sopenharmony_ci                // Oops, we hit a limit.
228ffe3c632Sopenharmony_ci                if (mustSucceed)
229ffe3c632Sopenharmony_ci                {
230ffe3c632Sopenharmony_ci                    throw InvalidProtocolBufferException.TruncatedMessage();
231ffe3c632Sopenharmony_ci                }
232ffe3c632Sopenharmony_ci                else
233ffe3c632Sopenharmony_ci                {
234ffe3c632Sopenharmony_ci                    return false;
235ffe3c632Sopenharmony_ci                }
236ffe3c632Sopenharmony_ci            }
237ffe3c632Sopenharmony_ci
238ffe3c632Sopenharmony_ci            Stream input = codedInputStream.InternalInputStream;
239ffe3c632Sopenharmony_ci
240ffe3c632Sopenharmony_ci            state.totalBytesRetired += state.bufferSize;
241ffe3c632Sopenharmony_ci
242ffe3c632Sopenharmony_ci            state.bufferPos = 0;
243ffe3c632Sopenharmony_ci            state.bufferSize = (input == null) ? 0 : input.Read(codedInputStream.InternalBuffer, 0, buffer.Length);
244ffe3c632Sopenharmony_ci            if (state.bufferSize < 0)
245ffe3c632Sopenharmony_ci            {
246ffe3c632Sopenharmony_ci                throw new InvalidOperationException("Stream.Read returned a negative count");
247ffe3c632Sopenharmony_ci            }
248ffe3c632Sopenharmony_ci            if (state.bufferSize == 0)
249ffe3c632Sopenharmony_ci            {
250ffe3c632Sopenharmony_ci                if (mustSucceed)
251ffe3c632Sopenharmony_ci                {
252ffe3c632Sopenharmony_ci                    throw InvalidProtocolBufferException.TruncatedMessage();
253ffe3c632Sopenharmony_ci                }
254ffe3c632Sopenharmony_ci                else
255ffe3c632Sopenharmony_ci                {
256ffe3c632Sopenharmony_ci                    return false;
257ffe3c632Sopenharmony_ci                }
258ffe3c632Sopenharmony_ci            }
259ffe3c632Sopenharmony_ci            else
260ffe3c632Sopenharmony_ci            {
261ffe3c632Sopenharmony_ci                RecomputeBufferSizeAfterLimit(ref state);
262ffe3c632Sopenharmony_ci                int totalBytesRead =
263ffe3c632Sopenharmony_ci                    state.totalBytesRetired + state.bufferSize + state.bufferSizeAfterLimit;
264ffe3c632Sopenharmony_ci                if (totalBytesRead < 0 || totalBytesRead > state.sizeLimit)
265ffe3c632Sopenharmony_ci                {
266ffe3c632Sopenharmony_ci                    throw InvalidProtocolBufferException.SizeLimitExceeded();
267ffe3c632Sopenharmony_ci                }
268ffe3c632Sopenharmony_ci                return true;
269ffe3c632Sopenharmony_ci            }
270ffe3c632Sopenharmony_ci        }
271ffe3c632Sopenharmony_ci
272ffe3c632Sopenharmony_ci        private static void RecomputeBufferSizeAfterLimit(ref ParserInternalState state)
273ffe3c632Sopenharmony_ci        {
274ffe3c632Sopenharmony_ci            state.bufferSize += state.bufferSizeAfterLimit;
275ffe3c632Sopenharmony_ci            int bufferEnd = state.totalBytesRetired + state.bufferSize;
276ffe3c632Sopenharmony_ci            if (bufferEnd > state.currentLimit)
277ffe3c632Sopenharmony_ci            {
278ffe3c632Sopenharmony_ci                // Limit is in current buffer.
279ffe3c632Sopenharmony_ci                state.bufferSizeAfterLimit = bufferEnd - state.currentLimit;
280ffe3c632Sopenharmony_ci                state.bufferSize -= state.bufferSizeAfterLimit;
281ffe3c632Sopenharmony_ci            }
282ffe3c632Sopenharmony_ci            else
283ffe3c632Sopenharmony_ci            {
284ffe3c632Sopenharmony_ci                state.bufferSizeAfterLimit = 0;
285ffe3c632Sopenharmony_ci            }
286ffe3c632Sopenharmony_ci        }
287ffe3c632Sopenharmony_ci
288ffe3c632Sopenharmony_ci        private static void CheckCurrentBufferIsEmpty(ref ParserInternalState state)
289ffe3c632Sopenharmony_ci        {
290ffe3c632Sopenharmony_ci            if (state.bufferPos < state.bufferSize)
291ffe3c632Sopenharmony_ci            {
292ffe3c632Sopenharmony_ci                throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
293ffe3c632Sopenharmony_ci            }
294ffe3c632Sopenharmony_ci        }
295ffe3c632Sopenharmony_ci    }
296ffe3c632Sopenharmony_ci}