• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }