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}