1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2008 Google Inc. All rights reserved. 4 // 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file or at 7 // https://developers.google.com/open-source/licenses/bsd 8 #endregion 9 10 using System; 11 using System.Buffers; 12 using System.Runtime.CompilerServices; 13 using System.Security; 14 15 namespace Google.Protobuf 16 { 17 /// <summary> 18 /// An opaque struct that represents the current parsing state and is passed along 19 /// as the parsing proceeds. 20 /// All the public methods are intended to be invoked only by the generated code, 21 /// users should never invoke them directly. 22 /// </summary> 23 [SecuritySafeCritical] 24 public ref struct ParseContext 25 { 26 internal const int DefaultRecursionLimit = 100; 27 internal const int DefaultSizeLimit = int.MaxValue; 28 29 internal ReadOnlySpan<byte> buffer; 30 internal ParserInternalState state; 31 32 /// <summary> 33 /// Initialize a <see cref="ParseContext"/>, building all <see cref="ParserInternalState"/> from defaults and 34 /// the given <paramref name="buffer"/>. 35 /// </summary> 36 [MethodImpl(MethodImplOptions.AggressiveInlining)] InitializeGoogle.Protobuf.ParseContext37 internal static void Initialize(ReadOnlySpan<byte> buffer, out ParseContext ctx) 38 { 39 ParserInternalState state = default; 40 state.sizeLimit = DefaultSizeLimit; 41 state.recursionLimit = DefaultRecursionLimit; 42 state.currentLimit = int.MaxValue; 43 state.bufferSize = buffer.Length; 44 // Equivalent to Initialize(buffer, ref state, out ctx); 45 ctx.buffer = buffer; 46 ctx.state = state; 47 } 48 49 /// <summary> 50 /// Initialize a <see cref="ParseContext"/> using existing <see cref="ParserInternalState"/>, e.g. from <see cref="CodedInputStream"/>. 51 /// </summary> 52 [MethodImpl(MethodImplOptions.AggressiveInlining)] InitializeGoogle.Protobuf.ParseContext53 internal static void Initialize(ReadOnlySpan<byte> buffer, ref ParserInternalState state, out ParseContext ctx) 54 { 55 // Note: if this code ever changes, also change the initialization above. 56 ctx.buffer = buffer; 57 ctx.state = state; 58 } 59 60 /// <summary> 61 /// Creates a ParseContext instance from CodedInputStream. 62 /// WARNING: internally this copies the CodedInputStream's state, so after done with the ParseContext, 63 /// the CodedInputStream's state needs to be updated. 64 /// </summary> 65 [MethodImpl(MethodImplOptions.AggressiveInlining)] InitializeGoogle.Protobuf.ParseContext66 internal static void Initialize(CodedInputStream input, out ParseContext ctx) 67 { 68 ctx.buffer = new ReadOnlySpan<byte>(input.InternalBuffer); 69 // ideally we would use a reference to the original state, but that doesn't seem possible 70 // so we just copy the struct that holds the state. We will need to later store the state back 71 // into CodedInputStream if we want to keep it usable. 72 ctx.state = input.InternalState; 73 } 74 75 [MethodImpl(MethodImplOptions.AggressiveInlining)] InitializeGoogle.Protobuf.ParseContext76 internal static void Initialize(ReadOnlySequence<byte> input, out ParseContext ctx) 77 { 78 Initialize(input, DefaultRecursionLimit, out ctx); 79 } 80 81 [MethodImpl(MethodImplOptions.AggressiveInlining)] InitializeGoogle.Protobuf.ParseContext82 internal static void Initialize(ReadOnlySequence<byte> input, int recursionLimit, out ParseContext ctx) 83 { 84 ctx.buffer = default; 85 ctx.state = default; 86 ctx.state.lastTag = 0; 87 ctx.state.recursionDepth = 0; 88 ctx.state.sizeLimit = DefaultSizeLimit; 89 ctx.state.recursionLimit = recursionLimit; 90 ctx.state.currentLimit = int.MaxValue; 91 SegmentedBufferHelper.Initialize(input, out ctx.state.segmentedBufferHelper, out ctx.buffer); 92 ctx.state.bufferPos = 0; 93 ctx.state.bufferSize = ctx.buffer.Length; 94 95 ctx.state.DiscardUnknownFields = false; 96 ctx.state.ExtensionRegistry = null; 97 } 98 99 /// <summary> 100 /// Returns the last tag read, or 0 if no tags have been read or we've read beyond 101 /// the end of the input. 102 /// </summary> 103 internal uint LastTag => state.lastTag; 104 105 /// <summary> 106 /// Internal-only property; when set to true, unknown fields will be discarded while parsing. 107 /// </summary> 108 internal bool DiscardUnknownFields 109 { 110 get => state.DiscardUnknownFields; 111 set => state.DiscardUnknownFields = value; 112 } 113 114 /// <summary> 115 /// Internal-only property; provides extension identifiers to compatible messages while parsing. 116 /// </summary> 117 internal ExtensionRegistry ExtensionRegistry 118 { 119 get => state.ExtensionRegistry; 120 set => state.ExtensionRegistry = value; 121 } 122 123 /// <summary> 124 /// Reads a field tag, returning the tag of 0 for "end of input". 125 /// </summary> 126 /// <remarks> 127 /// If this method returns 0, it doesn't necessarily mean the end of all 128 /// the data in this CodedInputReader; it may be the end of the logical input 129 /// for an embedded message, for example. 130 /// </remarks> 131 /// <returns>The next field tag, or 0 for end of input. (0 is never a valid tag.)</returns> 132 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadTagGoogle.Protobuf.ParseContext133 public uint ReadTag() => ParsingPrimitives.ParseTag(ref buffer, ref state); 134 135 /// <summary> 136 /// Reads a double field from the input. 137 /// </summary> 138 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadDoubleGoogle.Protobuf.ParseContext139 public double ReadDouble() => ParsingPrimitives.ParseDouble(ref buffer, ref state); 140 141 /// <summary> 142 /// Reads a float field from the input. 143 /// </summary> 144 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadFloatGoogle.Protobuf.ParseContext145 public float ReadFloat() => ParsingPrimitives.ParseFloat(ref buffer, ref state); 146 147 /// <summary> 148 /// Reads a uint64 field from the input. 149 /// </summary> 150 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadUInt64Google.Protobuf.ParseContext151 public ulong ReadUInt64() => ParsingPrimitives.ParseRawVarint64(ref buffer, ref state); 152 153 /// <summary> 154 /// Reads an int64 field from the input. 155 /// </summary> 156 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadInt64Google.Protobuf.ParseContext157 public long ReadInt64() => (long)ParsingPrimitives.ParseRawVarint64(ref buffer, ref state); 158 159 /// <summary> 160 /// Reads an int32 field from the input. 161 /// </summary> 162 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadInt32Google.Protobuf.ParseContext163 public int ReadInt32() => (int)ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 164 165 /// <summary> 166 /// Reads a fixed64 field from the input. 167 /// </summary> 168 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadFixed64Google.Protobuf.ParseContext169 public ulong ReadFixed64() => ParsingPrimitives.ParseRawLittleEndian64(ref buffer, ref state); 170 171 /// <summary> 172 /// Reads a fixed32 field from the input. 173 /// </summary> 174 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadFixed32Google.Protobuf.ParseContext175 public uint ReadFixed32() => ParsingPrimitives.ParseRawLittleEndian32(ref buffer, ref state); 176 177 /// <summary> 178 /// Reads a bool field from the input. 179 /// </summary> 180 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadBoolGoogle.Protobuf.ParseContext181 public bool ReadBool() => ParsingPrimitives.ParseRawVarint64(ref buffer, ref state) != 0; 182 183 /// <summary> 184 /// Reads a string field from the input. 185 /// </summary> 186 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadStringGoogle.Protobuf.ParseContext187 public string ReadString() => ParsingPrimitives.ReadString(ref buffer, ref state); 188 189 /// <summary> 190 /// Reads an embedded message field value from the input. 191 /// </summary> 192 [MethodImpl(MethodImplOptions.AggressiveInlining)] 193 public void ReadMessage(IMessage message) => ParsingPrimitivesMessages.ReadMessage(ref this, message); 194 195 /// <summary> 196 /// Reads an embedded group field from the input. 197 /// </summary> 198 [MethodImpl(MethodImplOptions.AggressiveInlining)] 199 public void ReadGroup(IMessage message) => ParsingPrimitivesMessages.ReadGroup(ref this, message); 200 201 /// <summary> 202 /// Reads a bytes field value from the input. 203 /// </summary> 204 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadBytesGoogle.Protobuf.ParseContext205 public ByteString ReadBytes() => ParsingPrimitives.ReadBytes(ref buffer, ref state); 206 207 /// <summary> 208 /// Reads a uint32 field value from the input. 209 /// </summary> 210 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadUInt32Google.Protobuf.ParseContext211 public uint ReadUInt32() => ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 212 213 /// <summary> 214 /// Reads an enum field value from the input. 215 /// </summary> 216 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadEnumGoogle.Protobuf.ParseContext217 public int ReadEnum() 218 { 219 // Currently just a pass-through, but it's nice to separate it logically from WriteInt32. 220 return (int)ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 221 } 222 223 /// <summary> 224 /// Reads an sfixed32 field value from the input. 225 /// </summary> 226 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadSFixed32Google.Protobuf.ParseContext227 public int ReadSFixed32() => (int)ParsingPrimitives.ParseRawLittleEndian32(ref buffer, ref state); 228 229 /// <summary> 230 /// Reads an sfixed64 field value from the input. 231 /// </summary> 232 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadSFixed64Google.Protobuf.ParseContext233 public long ReadSFixed64() => (long)ParsingPrimitives.ParseRawLittleEndian64(ref buffer, ref state); 234 235 /// <summary> 236 /// Reads an sint32 field value from the input. 237 /// </summary> 238 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadSInt32Google.Protobuf.ParseContext239 public int ReadSInt32() => ParsingPrimitives.DecodeZigZag32(ParsingPrimitives.ParseRawVarint32(ref buffer, ref state)); 240 241 /// <summary> 242 /// Reads an sint64 field value from the input. 243 /// </summary> 244 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadSInt64Google.Protobuf.ParseContext245 public long ReadSInt64() => ParsingPrimitives.DecodeZigZag64(ParsingPrimitives.ParseRawVarint64(ref buffer, ref state)); 246 247 /// <summary> 248 /// Reads a length for length-delimited data. 249 /// </summary> 250 /// <remarks> 251 /// This is internally just reading a varint, but this method exists 252 /// to make the calling code clearer. 253 /// </remarks> 254 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadLengthGoogle.Protobuf.ParseContext255 public int ReadLength() => (int)ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); 256 CopyStateToGoogle.Protobuf.ParseContext257 internal void CopyStateTo(CodedInputStream input) 258 { 259 input.InternalState = state; 260 } 261 LoadStateFromGoogle.Protobuf.ParseContext262 internal void LoadStateFrom(CodedInputStream input) 263 { 264 state = input.InternalState; 265 } 266 } 267 }