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