1#region Copyright notice and license 2// Protocol Buffers - Google's data interchange format 3// Copyright 2008 Google Inc. All rights reserved. 4// https://developers.google.com/protocol-buffers/ 5// 6// Redistribution and use in source and binary forms, with or without 7// modification, are permitted provided that the following conditions are 8// met: 9// 10// * Redistributions of source code must retain the above copyright 11// notice, this list of conditions and the following disclaimer. 12// * Redistributions in binary form must reproduce the above 13// copyright notice, this list of conditions and the following disclaimer 14// in the documentation and/or other materials provided with the 15// distribution. 16// * Neither the name of Google Inc. nor the names of its 17// contributors may be used to endorse or promote products derived from 18// this software without specific prior written permission. 19// 20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31#endregion 32 33using System; 34using System.Buffers; 35using System.Buffers.Binary; 36using System.Collections.Generic; 37using System.IO; 38using System.Runtime.CompilerServices; 39using System.Runtime.InteropServices; 40using System.Security; 41using System.Text; 42using Google.Protobuf.Collections; 43 44namespace Google.Protobuf 45{ 46 /// <summary> 47 /// An opaque struct that represents the current parsing state and is passed along 48 /// as the parsing proceeds. 49 /// All the public methods are intended to be invoked only by the generated code, 50 /// users should never invoke them directly. 51 /// </summary> 52 [SecuritySafeCritical] 53 public ref struct ParseContext 54 { 55 internal const int DefaultRecursionLimit = 100; 56 internal const int DefaultSizeLimit = Int32.MaxValue; 57 58 internal ReadOnlySpan<byte> buffer; 59 internal ParserInternalState state; 60 61 [MethodImpl(MethodImplOptions.AggressiveInlining)] 62 internal static void Initialize(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, out ParseContext ctx) 63 { 64 ctx.buffer = buffer; 65 ctx.state = state; 66 } 67 68 /// <summary> 69 /// Creates a ParseContext instance from CodedInputStream. 70 /// WARNING: internally this copies the CodedInputStream's state, so after done with the ParseContext, 71 /// the CodedInputStream's state needs to be updated. 72 /// </summary> 73 [MethodImpl(MethodImplOptions.AggressiveInlining)] 74 internal static void Initialize(CodedInputStream input, out ParseContext ctx) 75 { 76 ctx.buffer = new ReadOnlySpan<byte>(input.InternalBuffer); 77 // ideally we would use a reference to the original state, but that doesn't seem possible 78 // so we just copy the struct that holds the state. We will need to later store the state back 79 // into CodedInputStream if we want to keep it usable. 80 ctx.state = input.InternalState; 81 } 82 83 [MethodImpl(MethodImplOptions.AggressiveInlining)] 84 internal static void Initialize(ReadOnlySequence<byte> input, out ParseContext ctx) 85 { 86 Initialize(input, DefaultRecursionLimit, out ctx); 87 } 88 89 [MethodImpl(MethodImplOptions.AggressiveInlining)] 90 internal static void Initialize(ReadOnlySequence<byte> input, int recursionLimit, out ParseContext ctx) 91 { 92 ctx.buffer = default; 93 ctx.state = default; 94 ctx.state.lastTag = 0; 95 ctx.state.recursionDepth = 0; 96 ctx.state.sizeLimit = DefaultSizeLimit; 97 ctx.state.recursionLimit = recursionLimit; 98 ctx.state.currentLimit = int.MaxValue; 99 SegmentedBufferHelper.Initialize(input, out ctx.state.segmentedBufferHelper, out ctx.buffer); 100 ctx.state.bufferPos = 0; 101 ctx.state.bufferSize = ctx.buffer.Length; 102 103 ctx.state.DiscardUnknownFields = false; 104 ctx.state.ExtensionRegistry = null; 105 } 106 107 /// <summary> 108 /// Returns the last tag read, or 0 if no tags have been read or we've read beyond 109 /// the end of the input. 110 /// </summary> 111 internal uint LastTag { get { return state.lastTag; } } 112 113 /// <summary> 114 /// Internal-only property; when set to true, unknown fields will be discarded while parsing. 115 /// </summary> 116 internal bool DiscardUnknownFields { 117 get { return state.DiscardUnknownFields; } 118 set { state.DiscardUnknownFields = value; } 119 } 120 121 /// <summary> 122 /// Internal-only property; provides extension identifiers to compatible messages while parsing. 123 /// </summary> 124 internal ExtensionRegistry ExtensionRegistry 125 { 126 get { return state.ExtensionRegistry; } 127 set { state.ExtensionRegistry = value; } 128 } 129 130 /// <summary> 131 /// Reads a field tag, returning the tag of 0 for "end of input". 132 /// </summary> 133 /// <remarks> 134 /// If this method returns 0, it doesn't necessarily mean the end of all 135 /// the data in this CodedInputReader; it may be the end of the logical input 136 /// for an embedded message, for example. 137 /// </remarks> 138 /// <returns>The next field tag, or 0 for end of input. (0 is never a valid tag.)</returns> 139 [MethodImpl(MethodImplOptions.AggressiveInlining)] 140 public uint ReadTag() 141 { 142 return ParsingPrimitives.ParseTag(ref buffer, ref state); 143 } 144 145 /// <summary> 146 /// Reads a double field from the input. 147 /// </summary> 148 [MethodImpl(MethodImplOptions.AggressiveInlining)] 149 public double ReadDouble() 150 { 151 return ParsingPrimitives.ParseDouble(ref buffer, ref state); 152 } 153 154 /// <summary> 155 /// Reads a float field from the input. 156 /// </summary> 157 [MethodImpl(MethodImplOptions.AggressiveInlining)] 158 public float ReadFloat() 159 { 160 return ParsingPrimitives.ParseFloat(ref buffer, ref state); 161 } 162 163 /// <summary> 164 /// Reads a uint64 field from the input. 165 /// </summary> 166 [MethodImpl(MethodImplOptions.AggressiveInlining)] 167 public ulong ReadUInt64() 168 { 169 return ParsingPrimitives.ParseRawVarint64(ref buffer, ref state); 170 } 171 172 /// <summary> 173 /// Reads an int64 field from the input. 174 /// </summary> 175 [MethodImpl(MethodImplOptions.AggressiveInlining)] 176 public long ReadInt64() 177 { 178 return (long)ParsingPrimitives.ParseRawVarint64(ref buffer, ref state); 179 } 180 181 /// <summary> 182 /// Reads an int32 field from the input. 183 /// </summary> 184 [MethodImpl(MethodImplOptions.AggressiveInlining)] 185 public int ReadInt32() 186 { 187 return (int)ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 188 } 189 190 /// <summary> 191 /// Reads a fixed64 field from the input. 192 /// </summary> 193 [MethodImpl(MethodImplOptions.AggressiveInlining)] 194 public ulong ReadFixed64() 195 { 196 return ParsingPrimitives.ParseRawLittleEndian64(ref buffer, ref state); 197 } 198 199 /// <summary> 200 /// Reads a fixed32 field from the input. 201 /// </summary> 202 [MethodImpl(MethodImplOptions.AggressiveInlining)] 203 public uint ReadFixed32() 204 { 205 return ParsingPrimitives.ParseRawLittleEndian32(ref buffer, ref state); 206 } 207 208 /// <summary> 209 /// Reads a bool field from the input. 210 /// </summary> 211 [MethodImpl(MethodImplOptions.AggressiveInlining)] 212 public bool ReadBool() 213 { 214 return ParsingPrimitives.ParseRawVarint64(ref buffer, ref state) != 0; 215 } 216 /// <summary> 217 /// Reads a string field from the input. 218 /// </summary> 219 [MethodImpl(MethodImplOptions.AggressiveInlining)] 220 public string ReadString() 221 { 222 return ParsingPrimitives.ReadString(ref buffer, ref state); 223 } 224 225 /// <summary> 226 /// Reads an embedded message field value from the input. 227 /// </summary> 228 [MethodImpl(MethodImplOptions.AggressiveInlining)] 229 public void ReadMessage(IMessage message) 230 { 231 ParsingPrimitivesMessages.ReadMessage(ref this, message); 232 } 233 234 /// <summary> 235 /// Reads an embedded group field from the input. 236 /// </summary> 237 [MethodImpl(MethodImplOptions.AggressiveInlining)] 238 public void ReadGroup(IMessage message) 239 { 240 ParsingPrimitivesMessages.ReadGroup(ref this, message); 241 } 242 243 /// <summary> 244 /// Reads a bytes field value from the input. 245 /// </summary> 246 [MethodImpl(MethodImplOptions.AggressiveInlining)] 247 public ByteString ReadBytes() 248 { 249 return ParsingPrimitives.ReadBytes(ref buffer, ref state); 250 } 251 /// <summary> 252 /// Reads a uint32 field value from the input. 253 /// </summary> 254 [MethodImpl(MethodImplOptions.AggressiveInlining)] 255 public uint ReadUInt32() 256 { 257 return ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 258 } 259 260 /// <summary> 261 /// Reads an enum field value from the input. 262 /// </summary> 263 [MethodImpl(MethodImplOptions.AggressiveInlining)] 264 public int ReadEnum() 265 { 266 // Currently just a pass-through, but it's nice to separate it logically from WriteInt32. 267 return (int)ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 268 } 269 270 /// <summary> 271 /// Reads an sfixed32 field value from the input. 272 /// </summary> 273 [MethodImpl(MethodImplOptions.AggressiveInlining)] 274 public int ReadSFixed32() 275 { 276 return (int)ParsingPrimitives.ParseRawLittleEndian32(ref buffer, ref state); 277 } 278 279 /// <summary> 280 /// Reads an sfixed64 field value from the input. 281 /// </summary> 282 [MethodImpl(MethodImplOptions.AggressiveInlining)] 283 public long ReadSFixed64() 284 { 285 return (long)ParsingPrimitives.ParseRawLittleEndian64(ref buffer, ref state); 286 } 287 288 /// <summary> 289 /// Reads an sint32 field value from the input. 290 /// </summary> 291 [MethodImpl(MethodImplOptions.AggressiveInlining)] 292 public int ReadSInt32() 293 { 294 return ParsingPrimitives.DecodeZigZag32(ParsingPrimitives.ParseRawVarint32(ref buffer, ref state)); 295 } 296 297 /// <summary> 298 /// Reads an sint64 field value from the input. 299 /// </summary> 300 [MethodImpl(MethodImplOptions.AggressiveInlining)] 301 public long ReadSInt64() 302 { 303 return ParsingPrimitives.DecodeZigZag64(ParsingPrimitives.ParseRawVarint64(ref buffer, ref state)); 304 } 305 306 /// <summary> 307 /// Reads a length for length-delimited data. 308 /// </summary> 309 /// <remarks> 310 /// This is internally just reading a varint, but this method exists 311 /// to make the calling code clearer. 312 /// </remarks> 313 [MethodImpl(MethodImplOptions.AggressiveInlining)] 314 public int ReadLength() 315 { 316 return (int)ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 317 } 318 319 internal void CopyStateTo(CodedInputStream input) 320 { 321 input.InternalState = state; 322 } 323 324 internal void LoadStateFrom(CodedInputStream input) 325 { 326 state = input.InternalState; 327 } 328 } 329}