blob: c8b33b33ba9e55970a8a5126278dfd3232d653fa [file] [log] [blame]
csharptest71f662c2011-05-20 15:15:34 -05001#region Copyright notice and license
csharptest71f662c2011-05-20 15:15:34 -05002// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc. All rights reserved.
Jon Skeetee835a32015-06-30 17:22:26 +01004// https://developers.google.com/protocol-buffers/
csharptest71f662c2011-05-20 15:15:34 -05005//
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.
csharptest71f662c2011-05-20 15:15:34 -050031#endregion
32
Jon Skeet6f300442015-08-06 14:29:34 +010033using Google.Protobuf.Collections;
csharptest71f662c2011-05-20 15:15:34 -050034using System;
35using System.Collections.Generic;
36using System.IO;
csharptest71f662c2011-05-20 15:15:34 -050037
Jon Skeete38294a2015-06-09 19:30:44 +010038namespace Google.Protobuf
csharptest71f662c2011-05-20 15:15:34 -050039{
40 /// <summary>
41 /// Readings and decodes protocol message fields.
42 /// </summary>
43 /// <remarks>
Jon Skeet6f300442015-08-06 14:29:34 +010044 /// <para>
45 /// This class is generally used by generated code to read appropriate
46 /// primitives from the stream. It effectively encapsulates the lowest
47 /// levels of protocol buffer format.
48 /// </para>
49 /// <para>
50 /// Repeated fields and map fields are not handled by this class; use <see cref="RepeatedField{T}"/>
51 /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields.
52 /// </para>
csharptest71f662c2011-05-20 15:15:34 -050053 /// </remarks>
Jon Skeet96ddf012015-06-12 09:53:12 +010054 public sealed class CodedInputStream
csharptest71f662c2011-05-20 15:15:34 -050055 {
Jon Skeet6f300442015-08-06 14:29:34 +010056 // TODO(jonskeet): Consider whether recursion and size limits shouldn't be readonly,
57 // set at construction time.
58
Jon Skeet15bf55e2015-08-04 12:38:53 +010059 /// <summary>
60 /// Buffer of data read from the stream or provided at construction time.
61 /// </summary>
csharptest71f662c2011-05-20 15:15:34 -050062 private readonly byte[] buffer;
Jon Skeet15bf55e2015-08-04 12:38:53 +010063
64 /// <summary>
65 /// The number of valid bytes in the buffer.
66 /// </summary>
csharptest71f662c2011-05-20 15:15:34 -050067 private int bufferSize;
Jon Skeet15bf55e2015-08-04 12:38:53 +010068
csharptest71f662c2011-05-20 15:15:34 -050069 private int bufferSizeAfterLimit = 0;
Jon Skeet15bf55e2015-08-04 12:38:53 +010070 /// <summary>
71 /// The position within the current buffer (i.e. the next byte to read)
72 /// </summary>
csharptest71f662c2011-05-20 15:15:34 -050073 private int bufferPos = 0;
Jon Skeet15bf55e2015-08-04 12:38:53 +010074
75 /// <summary>
76 /// The stream to read further input from, or null if the byte array buffer was provided
77 /// directly on construction, with no further data available.
78 /// </summary>
csharptest71f662c2011-05-20 15:15:34 -050079 private readonly Stream input;
Jon Skeet15bf55e2015-08-04 12:38:53 +010080
81 /// <summary>
82 /// The last tag we read. 0 indicates we've read to the end of the stream
83 /// (or haven't read anything yet).
84 /// </summary>
csharptest71f662c2011-05-20 15:15:34 -050085 private uint lastTag = 0;
86
Jon Skeet15bf55e2015-08-04 12:38:53 +010087 /// <summary>
88 /// The next tag, used to store the value read by PeekTag.
89 /// </summary>
csharptest367e0222011-06-09 12:47:46 -050090 private uint nextTag = 0;
91 private bool hasNextTag = false;
92
csharptest71f662c2011-05-20 15:15:34 -050093 internal const int DefaultRecursionLimit = 64;
94 internal const int DefaultSizeLimit = 64 << 20; // 64MB
Jon Skeet811fc892015-08-04 15:58:39 +010095 internal const int BufferSize = 4096;
csharptest71f662c2011-05-20 15:15:34 -050096
97 /// <summary>
98 /// The total number of bytes read before the current buffer. The
99 /// total bytes read up to the current position can be computed as
100 /// totalBytesRetired + bufferPos.
101 /// </summary>
102 private int totalBytesRetired = 0;
103
104 /// <summary>
105 /// The absolute position of the end of the current message.
106 /// </summary>
107 private int currentLimit = int.MaxValue;
108
109 /// <summary>
110 /// <see cref="SetRecursionLimit"/>
111 /// </summary>
112 private int recursionDepth = 0;
113
114 private int recursionLimit = DefaultRecursionLimit;
115
116 /// <summary>
117 /// <see cref="SetSizeLimit"/>
118 /// </summary>
119 private int sizeLimit = DefaultSizeLimit;
120
121 #region Construction
csharptest71f662c2011-05-20 15:15:34 -0500122 /// <summary>
123 /// Creates a new CodedInputStream reading data from the given
124 /// byte array.
125 /// </summary>
Jon Skeet0e0e0c92015-08-03 11:08:53 +0100126 public CodedInputStream(byte[] buf) : this(buf, 0, buf.Length)
csharptest71f662c2011-05-20 15:15:34 -0500127 {
csharptest71f662c2011-05-20 15:15:34 -0500128 }
129
130 /// <summary>
131 /// Creates a new CodedInputStream that reads from the given
132 /// byte array slice.
133 /// </summary>
Jon Skeet0e0e0c92015-08-03 11:08:53 +0100134 public CodedInputStream(byte[] buffer, int offset, int length)
csharptest71f662c2011-05-20 15:15:34 -0500135 {
136 this.buffer = buffer;
137 this.bufferPos = offset;
138 this.bufferSize = offset + length;
139 this.input = null;
140 }
141
Jon Skeet0e0e0c92015-08-03 11:08:53 +0100142 /// <summary>
143 /// Creates a new CodedInputStream reading data from the given stream.
144 /// </summary>
145 public CodedInputStream(Stream input)
csharptest71f662c2011-05-20 15:15:34 -0500146 {
147 this.buffer = new byte[BufferSize];
148 this.bufferSize = 0;
149 this.input = input;
150 }
151
Jon Skeet0e0e0c92015-08-03 11:08:53 +0100152 /// <summary>
153 /// Creates a new CodedInputStream reading data from the given
154 /// stream, with a pre-allocated buffer.
155 /// </summary>
156 internal CodedInputStream(Stream input, byte[] buffer)
csharptest92fcf352013-05-07 15:56:50 -0500157 {
158 this.buffer = buffer;
159 this.bufferSize = 0;
160 this.input = input;
161 }
csharptest71f662c2011-05-20 15:15:34 -0500162 #endregion
163
csharptest92fcf352013-05-07 15:56:50 -0500164 /// <summary>
165 /// Returns the current position in the input stream, or the position in the input buffer
166 /// </summary>
167 public long Position
168 {
169 get
170 {
171 if (input != null)
csharptestd63096d2013-09-21 13:51:04 -0500172 {
csharptest0a0dd032013-05-16 13:59:34 -0500173 return input.Position - ((bufferSize + bufferSizeAfterLimit) - bufferPos);
csharptestd63096d2013-09-21 13:51:04 -0500174 }
csharptest92fcf352013-05-07 15:56:50 -0500175 return bufferPos;
176 }
177 }
178
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100179 /// <summary>
180 /// Returns the last tag read, or 0 if no tags have been read or we've read beyond
181 /// the end of the stream.
182 /// </summary>
183 internal uint LastTag { get { return lastTag; } }
184
Jon Skeetf34d37a2015-06-30 13:16:20 +0100185 #region Limits for recursion and length
186 /// <summary>
187 /// Set the maximum message recursion depth.
188 /// </summary>
189 /// <remarks>
190 /// In order to prevent malicious
191 /// messages from causing stack overflows, CodedInputStream limits
192 /// how deeply messages may be nested. The default limit is 64.
193 /// </remarks>
194 public int SetRecursionLimit(int limit)
195 {
196 if (limit < 0)
197 {
198 throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
199 }
200 int oldLimit = recursionLimit;
201 recursionLimit = limit;
202 return oldLimit;
203 }
csharptest71f662c2011-05-20 15:15:34 -0500204
205 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100206 /// Set the maximum message size.
207 /// </summary>
208 /// <remarks>
209 /// In order to prevent malicious messages from exhausting memory or
210 /// causing integer overflows, CodedInputStream limits how large a message may be.
211 /// The default limit is 64MB. You should set this limit as small
212 /// as you can without harming your app's functionality. Note that
213 /// size limits only apply when reading from an InputStream, not
214 /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
215 /// If you want to read several messages from a single CodedInputStream, you
216 /// can call ResetSizeCounter() after each message to avoid hitting the
217 /// size limit.
218 /// </remarks>
219 public int SetSizeLimit(int limit)
220 {
221 if (limit < 0)
222 {
223 throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
224 }
225 int oldLimit = sizeLimit;
226 sizeLimit = limit;
227 return oldLimit;
228 }
229
230 /// <summary>
231 /// Resets the current size counter to zero (see <see cref="SetSizeLimit"/>).
232 /// </summary>
233 public void ResetSizeCounter()
234 {
235 totalBytesRetired = 0;
236 }
237 #endregion
238
239 #region Validation
240 /// <summary>
Jon Skeete7f88ff2015-08-06 11:40:32 +0100241 /// Verifies that the last call to ReadTag() returned tag 0 - in other words,
242 /// we've reached the end of the stream when we expected to.
csharptest71f662c2011-05-20 15:15:34 -0500243 /// </summary>
Jon Skeete7f88ff2015-08-06 11:40:32 +0100244 /// <exception cref="InvalidProtocolBufferException">The
csharptest71f662c2011-05-20 15:15:34 -0500245 /// tag read was not the one specified</exception>
Jon Skeete7f88ff2015-08-06 11:40:32 +0100246 internal void CheckReadEndOfStreamTag()
csharptest71f662c2011-05-20 15:15:34 -0500247 {
Jon Skeete7f88ff2015-08-06 11:40:32 +0100248 if (lastTag != 0)
csharptest71f662c2011-05-20 15:15:34 -0500249 {
Jon Skeete7f88ff2015-08-06 11:40:32 +0100250 throw InvalidProtocolBufferException.MoreDataAvailable();
csharptest71f662c2011-05-20 15:15:34 -0500251 }
252 }
csharptest71f662c2011-05-20 15:15:34 -0500253 #endregion
254
255 #region Reading of tags etc
csharptest74c5e0c2011-07-14 13:06:22 -0500256
csharptest71f662c2011-05-20 15:15:34 -0500257 /// <summary>
Jon Skeetff334a62015-08-05 11:23:38 +0100258 /// Peeks at the next field tag. This is like calling <see cref="ReadTag"/>, but the
259 /// tag is not consumed. (So a subsequent call to <see cref="ReadTag"/> will return the
260 /// same value.)
csharptest367e0222011-06-09 12:47:46 -0500261 /// </summary>
Jon Skeetff334a62015-08-05 11:23:38 +0100262 public uint PeekTag()
csharptest367e0222011-06-09 12:47:46 -0500263 {
264 if (hasNextTag)
265 {
Jon Skeetff334a62015-08-05 11:23:38 +0100266 return nextTag;
csharptest367e0222011-06-09 12:47:46 -0500267 }
268
269 uint savedLast = lastTag;
Jon Skeetff334a62015-08-05 11:23:38 +0100270 nextTag = ReadTag();
271 hasNextTag = true;
272 lastTag = savedLast; // Undo the side effect of ReadTag
273 return nextTag;
csharptest367e0222011-06-09 12:47:46 -0500274 }
275
276 /// <summary>
Jon Skeetff334a62015-08-05 11:23:38 +0100277 /// Reads a field tag, returning the tag of 0 for "end of stream".
csharptest71f662c2011-05-20 15:15:34 -0500278 /// </summary>
Jon Skeete7f88ff2015-08-06 11:40:32 +0100279 /// <remarks>
280 /// If this method returns 0, it doesn't necessarily mean the end of all
281 /// the data in this CodedInputStream; it may be the end of the logical stream
282 /// for an embedded message, for example.
283 /// </remarks>
Jon Skeetff334a62015-08-05 11:23:38 +0100284 /// <returns>The next field tag, or 0 for end of stream. (0 is never a valid tag.)</returns>
285 public uint ReadTag()
csharptest71f662c2011-05-20 15:15:34 -0500286 {
csharptest367e0222011-06-09 12:47:46 -0500287 if (hasNextTag)
288 {
Jon Skeetff334a62015-08-05 11:23:38 +0100289 lastTag = nextTag;
csharptest367e0222011-06-09 12:47:46 -0500290 hasNextTag = false;
Jon Skeetff334a62015-08-05 11:23:38 +0100291 return lastTag;
csharptest367e0222011-06-09 12:47:46 -0500292 }
293
Jon Skeetf34d37a2015-06-30 13:16:20 +0100294 // Optimize for the incredibly common case of having at least two bytes left in the buffer,
295 // and those two bytes being enough to get the tag. This will be true for fields up to 4095.
296 if (bufferPos + 2 <= bufferSize)
csharptest71f662c2011-05-20 15:15:34 -0500297 {
Jon Skeetf34d37a2015-06-30 13:16:20 +0100298 int tmp = buffer[bufferPos++];
299 if (tmp < 128)
300 {
Jon Skeetff334a62015-08-05 11:23:38 +0100301 lastTag = (uint)tmp;
Jon Skeetf34d37a2015-06-30 13:16:20 +0100302 }
303 else
304 {
305 int result = tmp & 0x7f;
306 if ((tmp = buffer[bufferPos++]) < 128)
307 {
308 result |= tmp << 7;
Jon Skeetff334a62015-08-05 11:23:38 +0100309 lastTag = (uint) result;
Jon Skeetf34d37a2015-06-30 13:16:20 +0100310 }
311 else
312 {
313 // Nope, rewind and go the potentially slow route.
314 bufferPos -= 2;
Jon Skeetff334a62015-08-05 11:23:38 +0100315 lastTag = ReadRawVarint32();
Jon Skeetf34d37a2015-06-30 13:16:20 +0100316 }
317 }
csharptest71f662c2011-05-20 15:15:34 -0500318 }
Jon Skeetf34d37a2015-06-30 13:16:20 +0100319 else
320 {
321 if (IsAtEnd)
322 {
Jon Skeetff334a62015-08-05 11:23:38 +0100323 lastTag = 0;
324 return 0; // This is the only case in which we return 0.
Jon Skeetf34d37a2015-06-30 13:16:20 +0100325 }
csharptest71f662c2011-05-20 15:15:34 -0500326
Jon Skeetff334a62015-08-05 11:23:38 +0100327 lastTag = ReadRawVarint32();
Jon Skeetf34d37a2015-06-30 13:16:20 +0100328 }
csharptest71f662c2011-05-20 15:15:34 -0500329 if (lastTag == 0)
330 {
331 // If we actually read zero, that's not a valid tag.
332 throw InvalidProtocolBufferException.InvalidTag();
333 }
Jon Skeetff334a62015-08-05 11:23:38 +0100334 return lastTag;
csharptest71f662c2011-05-20 15:15:34 -0500335 }
336
337 /// <summary>
Jon Skeete7f88ff2015-08-06 11:40:32 +0100338 /// Skips the data for the field with the tag we've just read.
Jon Skeet9df2def2015-08-04 11:26:48 +0100339 /// This should be called directly after <see cref="ReadTag"/>, when
340 /// the caller wishes to skip an unknown field.
341 /// </summary>
Jon Skeete7f88ff2015-08-06 11:40:32 +0100342 public void SkipLastField()
Jon Skeet9df2def2015-08-04 11:26:48 +0100343 {
344 if (lastTag == 0)
345 {
Jon Skeete7f88ff2015-08-06 11:40:32 +0100346 throw new InvalidOperationException("SkipLastField cannot be called at the end of a stream");
Jon Skeet9df2def2015-08-04 11:26:48 +0100347 }
348 switch (WireFormat.GetTagWireType(lastTag))
349 {
350 case WireFormat.WireType.StartGroup:
Jon Skeet6e160372015-08-08 07:24:28 +0100351 SkipGroup();
Jon Skeete7f88ff2015-08-06 11:40:32 +0100352 break;
Jon Skeet9df2def2015-08-04 11:26:48 +0100353 case WireFormat.WireType.EndGroup:
Jon Skeete7f88ff2015-08-06 11:40:32 +0100354 // Just ignore; there's no data following the tag.
355 break;
Jon Skeet9df2def2015-08-04 11:26:48 +0100356 case WireFormat.WireType.Fixed32:
357 ReadFixed32();
358 break;
359 case WireFormat.WireType.Fixed64:
360 ReadFixed64();
361 break;
362 case WireFormat.WireType.LengthDelimited:
363 var length = ReadLength();
364 SkipRawBytes(length);
365 break;
366 case WireFormat.WireType.Varint:
367 ReadRawVarint32();
368 break;
369 }
370 }
371
Jon Skeet6e160372015-08-08 07:24:28 +0100372 private void SkipGroup()
Jon Skeete7f88ff2015-08-06 11:40:32 +0100373 {
374 // Note: Currently we expect this to be the way that groups are read. We could put the recursion
375 // depth changes into the ReadTag method instead, potentially...
376 recursionDepth++;
377 if (recursionDepth >= recursionLimit)
378 {
379 throw InvalidProtocolBufferException.RecursionLimitExceeded();
380 }
381 uint tag;
382 do
383 {
384 tag = ReadTag();
385 if (tag == 0)
386 {
387 throw InvalidProtocolBufferException.TruncatedMessage();
388 }
389 // This recursion will allow us to handle nested groups.
390 SkipLastField();
391 } while (WireFormat.GetTagWireType(tag) != WireFormat.WireType.EndGroup);
392 recursionDepth--;
393 }
394
Jon Skeet9df2def2015-08-04 11:26:48 +0100395 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100396 /// Reads a double field from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500397 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100398 public double ReadDouble()
csharptest71f662c2011-05-20 15:15:34 -0500399 {
Jon Skeetcdeda4b2015-06-19 17:30:13 +0100400 return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
csharptest71f662c2011-05-20 15:15:34 -0500401 }
402
403 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100404 /// Reads a float field from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500405 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100406 public float ReadFloat()
csharptest71f662c2011-05-20 15:15:34 -0500407 {
csharptest4ba365d2011-06-08 17:40:51 -0500408 if (BitConverter.IsLittleEndian && 4 <= bufferSize - bufferPos)
409 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100410 float ret = BitConverter.ToSingle(buffer, bufferPos);
csharptest4ba365d2011-06-08 17:40:51 -0500411 bufferPos += 4;
Jon Skeet96ddf012015-06-12 09:53:12 +0100412 return ret;
csharptest4ba365d2011-06-08 17:40:51 -0500413 }
414 else
415 {
416 byte[] rawBytes = ReadRawBytes(4);
417 if (!BitConverter.IsLittleEndian)
csharptest74c5e0c2011-07-14 13:06:22 -0500418 {
csharptestaef072a2011-06-08 18:00:43 -0500419 ByteArray.Reverse(rawBytes);
csharptest74c5e0c2011-07-14 13:06:22 -0500420 }
Jon Skeet96ddf012015-06-12 09:53:12 +0100421 return BitConverter.ToSingle(rawBytes, 0);
csharptest4ba365d2011-06-08 17:40:51 -0500422 }
csharptest71f662c2011-05-20 15:15:34 -0500423 }
424
425 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100426 /// Reads a uint64 field from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500427 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100428 public ulong ReadUInt64()
csharptest71f662c2011-05-20 15:15:34 -0500429 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100430 return ReadRawVarint64();
csharptest71f662c2011-05-20 15:15:34 -0500431 }
432
433 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100434 /// Reads an int64 field from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500435 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100436 public long ReadInt64()
csharptest71f662c2011-05-20 15:15:34 -0500437 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100438 return (long) ReadRawVarint64();
csharptest71f662c2011-05-20 15:15:34 -0500439 }
440
441 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100442 /// Reads an int32 field from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500443 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100444 public int ReadInt32()
csharptest71f662c2011-05-20 15:15:34 -0500445 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100446 return (int) ReadRawVarint32();
csharptest71f662c2011-05-20 15:15:34 -0500447 }
448
449 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100450 /// Reads a fixed64 field from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500451 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100452 public ulong ReadFixed64()
csharptest71f662c2011-05-20 15:15:34 -0500453 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100454 return ReadRawLittleEndian64();
csharptest71f662c2011-05-20 15:15:34 -0500455 }
456
457 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100458 /// Reads a fixed32 field from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500459 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100460 public uint ReadFixed32()
csharptest71f662c2011-05-20 15:15:34 -0500461 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100462 return ReadRawLittleEndian32();
csharptest71f662c2011-05-20 15:15:34 -0500463 }
464
465 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100466 /// Reads a bool field from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500467 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100468 public bool ReadBool()
csharptest71f662c2011-05-20 15:15:34 -0500469 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100470 return ReadRawVarint32() != 0;
csharptest71f662c2011-05-20 15:15:34 -0500471 }
472
473 /// <summary>
474 /// Reads a string field from the stream.
475 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100476 public string ReadString()
csharptest71f662c2011-05-20 15:15:34 -0500477 {
Jon Skeetf34d37a2015-06-30 13:16:20 +0100478 int length = ReadLength();
csharptest71f662c2011-05-20 15:15:34 -0500479 // No need to read any data for an empty string.
Jon Skeetf34d37a2015-06-30 13:16:20 +0100480 if (length == 0)
csharptest71f662c2011-05-20 15:15:34 -0500481 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100482 return "";
csharptest71f662c2011-05-20 15:15:34 -0500483 }
Jon Skeetf34d37a2015-06-30 13:16:20 +0100484 if (length <= bufferSize - bufferPos)
csharptest71f662c2011-05-20 15:15:34 -0500485 {
486 // Fast path: We already have the bytes in a contiguous buffer, so
487 // just copy directly from it.
Jon Skeetf34d37a2015-06-30 13:16:20 +0100488 String result = CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, length);
489 bufferPos += length;
Jon Skeet96ddf012015-06-12 09:53:12 +0100490 return result;
csharptest71f662c2011-05-20 15:15:34 -0500491 }
492 // Slow path: Build a byte array first then copy it.
Jon Skeetf34d37a2015-06-30 13:16:20 +0100493 return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length), 0, length);
csharptest71f662c2011-05-20 15:15:34 -0500494 }
495
496 /// <summary>
csharptest71f662c2011-05-20 15:15:34 -0500497 /// Reads an embedded message field value from the stream.
498 /// </summary>
Jon Skeete38294a2015-06-09 19:30:44 +0100499 public void ReadMessage(IMessage builder)
csharptest71f662c2011-05-20 15:15:34 -0500500 {
Jon Skeetf34d37a2015-06-30 13:16:20 +0100501 int length = ReadLength();
csharptest71f662c2011-05-20 15:15:34 -0500502 if (recursionDepth >= recursionLimit)
503 {
504 throw InvalidProtocolBufferException.RecursionLimitExceeded();
505 }
506 int oldLimit = PushLimit(length);
507 ++recursionDepth;
Jon Skeete38294a2015-06-09 19:30:44 +0100508 builder.MergeFrom(this);
Jon Skeete7f88ff2015-08-06 11:40:32 +0100509 CheckReadEndOfStreamTag();
Jon Skeet15bf55e2015-08-04 12:38:53 +0100510 // Check that we've read exactly as much data as expected.
511 if (!ReachedLimit)
512 {
513 throw InvalidProtocolBufferException.TruncatedMessage();
514 }
csharptest71f662c2011-05-20 15:15:34 -0500515 --recursionDepth;
516 PopLimit(oldLimit);
517 }
518
519 /// <summary>
520 /// Reads a bytes field value from the stream.
521 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100522 public ByteString ReadBytes()
csharptest71f662c2011-05-20 15:15:34 -0500523 {
Jon Skeetf34d37a2015-06-30 13:16:20 +0100524 int length = ReadLength();
525 if (length <= bufferSize - bufferPos && length > 0)
csharptest71f662c2011-05-20 15:15:34 -0500526 {
527 // Fast path: We already have the bytes in a contiguous buffer, so
528 // just copy directly from it.
Jon Skeetf34d37a2015-06-30 13:16:20 +0100529 ByteString result = ByteString.CopyFrom(buffer, bufferPos, length);
530 bufferPos += length;
Jon Skeet96ddf012015-06-12 09:53:12 +0100531 return result;
csharptest71f662c2011-05-20 15:15:34 -0500532 }
533 else
534 {
csharptest84f7e092011-07-14 13:20:21 -0500535 // Slow path: Build a byte array and attach it to a new ByteString.
Jon Skeetf34d37a2015-06-30 13:16:20 +0100536 return ByteString.AttachBytes(ReadRawBytes(length));
csharptest71f662c2011-05-20 15:15:34 -0500537 }
538 }
539
540 /// <summary>
541 /// Reads a uint32 field value from the stream.
542 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100543 public uint ReadUInt32()
csharptest71f662c2011-05-20 15:15:34 -0500544 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100545 return ReadRawVarint32();
csharptest71f662c2011-05-20 15:15:34 -0500546 }
547
548 /// <summary>
csharptestd2af9e92011-06-03 21:35:02 -0500549 /// Reads an enum field value from the stream. If the enum is valid for type T,
Jon Skeet9dc7d722015-01-31 08:56:52 +0000550 /// then the ref value is set and it returns true. Otherwise the unknown output
csharptestd2af9e92011-06-03 21:35:02 -0500551 /// value is set and this method returns false.
552 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100553 public int ReadEnum()
csharptestd2af9e92011-06-03 21:35:02 -0500554 {
Jon Skeet39aaf212015-06-11 21:15:36 +0100555 // Currently just a pass-through, but it's nice to separate it logically from WriteInt32.
Jon Skeet96ddf012015-06-12 09:53:12 +0100556 return (int) ReadRawVarint32();
csharptest71f662c2011-05-20 15:15:34 -0500557 }
558
559 /// <summary>
560 /// Reads an sfixed32 field value from the stream.
561 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100562 public int ReadSFixed32()
csharptest71f662c2011-05-20 15:15:34 -0500563 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100564 return (int) ReadRawLittleEndian32();
csharptest71f662c2011-05-20 15:15:34 -0500565 }
566
567 /// <summary>
568 /// Reads an sfixed64 field value from the stream.
569 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100570 public long ReadSFixed64()
csharptest71f662c2011-05-20 15:15:34 -0500571 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100572 return (long) ReadRawLittleEndian64();
csharptest71f662c2011-05-20 15:15:34 -0500573 }
574
575 /// <summary>
576 /// Reads an sint32 field value from the stream.
577 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100578 public int ReadSInt32()
csharptest71f662c2011-05-20 15:15:34 -0500579 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100580 return DecodeZigZag32(ReadRawVarint32());
csharptest71f662c2011-05-20 15:15:34 -0500581 }
582
583 /// <summary>
584 /// Reads an sint64 field value from the stream.
585 /// </summary>
Jon Skeet96ddf012015-06-12 09:53:12 +0100586 public long ReadSInt64()
csharptest71f662c2011-05-20 15:15:34 -0500587 {
Jon Skeet96ddf012015-06-12 09:53:12 +0100588 return DecodeZigZag64(ReadRawVarint64());
csharptestd2af9e92011-06-03 21:35:02 -0500589 }
590
Jon Skeete8c76bd2011-08-07 18:17:38 +0100591 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100592 /// Reads a length for length-delimited data.
593 /// </summary>
594 /// <remarks>
595 /// This is internally just reading a varint, but this method exists
596 /// to make the calling code clearer.
597 /// </remarks>
598 public int ReadLength()
599 {
600 return (int) ReadRawVarint32();
601 }
602
603 /// <summary>
Jon Skeetdf44ae42015-06-25 12:08:18 +0100604 /// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>,
605 /// the tag is consumed and the method returns <c>true</c>; otherwise, the
606 /// stream is left in the original position and the method returns <c>false</c>.
Jon Skeete8c76bd2011-08-07 18:17:38 +0100607 /// </summary>
Jon Skeetdf44ae42015-06-25 12:08:18 +0100608 public bool MaybeConsumeTag(uint tag)
Jon Skeetcdb45182011-08-07 18:03:40 +0100609 {
Jon Skeetff334a62015-08-05 11:23:38 +0100610 if (PeekTag() == tag)
Jon Skeetcdb45182011-08-07 18:03:40 +0100611 {
Jon Skeetff334a62015-08-05 11:23:38 +0100612 hasNextTag = false;
613 return true;
Jon Skeetcdb45182011-08-07 18:03:40 +0100614 }
615 return false;
616 }
617
csharptest71f662c2011-05-20 15:15:34 -0500618 #endregion
619
620 #region Underlying reading primitives
621
622 /// <summary>
623 /// Same code as ReadRawVarint32, but read each byte individually, checking for
624 /// buffer overflow.
625 /// </summary>
626 private uint SlowReadRawVarint32()
627 {
628 int tmp = ReadRawByte();
629 if (tmp < 128)
630 {
631 return (uint) tmp;
632 }
633 int result = tmp & 0x7f;
634 if ((tmp = ReadRawByte()) < 128)
635 {
636 result |= tmp << 7;
637 }
638 else
639 {
640 result |= (tmp & 0x7f) << 7;
641 if ((tmp = ReadRawByte()) < 128)
642 {
643 result |= tmp << 14;
644 }
645 else
646 {
647 result |= (tmp & 0x7f) << 14;
648 if ((tmp = ReadRawByte()) < 128)
649 {
650 result |= tmp << 21;
651 }
652 else
653 {
654 result |= (tmp & 0x7f) << 21;
655 result |= (tmp = ReadRawByte()) << 28;
656 if (tmp >= 128)
657 {
658 // Discard upper 32 bits.
659 for (int i = 0; i < 5; i++)
660 {
csharptest74c5e0c2011-07-14 13:06:22 -0500661 if (ReadRawByte() < 128)
662 {
663 return (uint) result;
664 }
csharptest71f662c2011-05-20 15:15:34 -0500665 }
666 throw InvalidProtocolBufferException.MalformedVarint();
667 }
668 }
669 }
670 }
671 return (uint) result;
672 }
673
674 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100675 /// Reads a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
csharptest71f662c2011-05-20 15:15:34 -0500676 /// This method is optimised for the case where we've got lots of data in the buffer.
677 /// That means we can check the size just once, then just read directly from the buffer
678 /// without constant rechecking of the buffer length.
679 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100680 internal uint ReadRawVarint32()
csharptest71f662c2011-05-20 15:15:34 -0500681 {
682 if (bufferPos + 5 > bufferSize)
683 {
684 return SlowReadRawVarint32();
685 }
686
687 int tmp = buffer[bufferPos++];
688 if (tmp < 128)
689 {
690 return (uint) tmp;
691 }
692 int result = tmp & 0x7f;
693 if ((tmp = buffer[bufferPos++]) < 128)
694 {
695 result |= tmp << 7;
696 }
697 else
698 {
699 result |= (tmp & 0x7f) << 7;
700 if ((tmp = buffer[bufferPos++]) < 128)
701 {
702 result |= tmp << 14;
703 }
704 else
705 {
706 result |= (tmp & 0x7f) << 14;
707 if ((tmp = buffer[bufferPos++]) < 128)
708 {
709 result |= tmp << 21;
710 }
711 else
712 {
713 result |= (tmp & 0x7f) << 21;
714 result |= (tmp = buffer[bufferPos++]) << 28;
715 if (tmp >= 128)
716 {
717 // Discard upper 32 bits.
718 // Note that this has to use ReadRawByte() as we only ensure we've
719 // got at least 5 bytes at the start of the method. This lets us
720 // use the fast path in more cases, and we rarely hit this section of code.
721 for (int i = 0; i < 5; i++)
722 {
csharptest74c5e0c2011-07-14 13:06:22 -0500723 if (ReadRawByte() < 128)
724 {
725 return (uint) result;
726 }
csharptest71f662c2011-05-20 15:15:34 -0500727 }
728 throw InvalidProtocolBufferException.MalformedVarint();
729 }
730 }
731 }
732 }
733 return (uint) result;
734 }
735
736 /// <summary>
737 /// Reads a varint from the input one byte at a time, so that it does not
738 /// read any bytes after the end of the varint. If you simply wrapped the
Jon Skeetf34d37a2015-06-30 13:16:20 +0100739 /// stream in a CodedInputStream and used ReadRawVarint32(Stream)
csharptest71f662c2011-05-20 15:15:34 -0500740 /// then you would probably end up reading past the end of the varint since
741 /// CodedInputStream buffers its input.
742 /// </summary>
743 /// <param name="input"></param>
744 /// <returns></returns>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100745 internal static uint ReadRawVarint32(Stream input)
csharptest71f662c2011-05-20 15:15:34 -0500746 {
747 int result = 0;
748 int offset = 0;
749 for (; offset < 32; offset += 7)
750 {
751 int b = input.ReadByte();
752 if (b == -1)
753 {
754 throw InvalidProtocolBufferException.TruncatedMessage();
755 }
756 result |= (b & 0x7f) << offset;
757 if ((b & 0x80) == 0)
758 {
759 return (uint) result;
760 }
761 }
762 // Keep reading up to 64 bits.
763 for (; offset < 64; offset += 7)
764 {
765 int b = input.ReadByte();
766 if (b == -1)
767 {
768 throw InvalidProtocolBufferException.TruncatedMessage();
769 }
770 if ((b & 0x80) == 0)
771 {
772 return (uint) result;
773 }
774 }
775 throw InvalidProtocolBufferException.MalformedVarint();
776 }
777
778 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100779 /// Reads a raw varint from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500780 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100781 internal ulong ReadRawVarint64()
csharptest71f662c2011-05-20 15:15:34 -0500782 {
783 int shift = 0;
784 ulong result = 0;
785 while (shift < 64)
786 {
787 byte b = ReadRawByte();
788 result |= (ulong) (b & 0x7F) << shift;
789 if ((b & 0x80) == 0)
790 {
791 return result;
792 }
793 shift += 7;
794 }
795 throw InvalidProtocolBufferException.MalformedVarint();
796 }
797
798 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100799 /// Reads a 32-bit little-endian integer from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500800 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100801 internal uint ReadRawLittleEndian32()
csharptest71f662c2011-05-20 15:15:34 -0500802 {
803 uint b1 = ReadRawByte();
804 uint b2 = ReadRawByte();
805 uint b3 = ReadRawByte();
806 uint b4 = ReadRawByte();
807 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
808 }
809
810 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100811 /// Reads a 64-bit little-endian integer from the stream.
csharptest71f662c2011-05-20 15:15:34 -0500812 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100813 internal ulong ReadRawLittleEndian64()
csharptest71f662c2011-05-20 15:15:34 -0500814 {
815 ulong b1 = ReadRawByte();
816 ulong b2 = ReadRawByte();
817 ulong b3 = ReadRawByte();
818 ulong b4 = ReadRawByte();
819 ulong b5 = ReadRawByte();
820 ulong b6 = ReadRawByte();
821 ulong b7 = ReadRawByte();
822 ulong b8 = ReadRawByte();
823 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
824 | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
825 }
826
Jon Skeetf34d37a2015-06-30 13:16:20 +0100827 /// <summary>
828 /// Decode a 32-bit value with ZigZag encoding.
829 /// </summary>
830 /// <remarks>
831 /// ZigZag encodes signed integers into values that can be efficiently
832 /// encoded with varint. (Otherwise, negative values must be
833 /// sign-extended to 64 bits to be varint encoded, thus always taking
834 /// 10 bytes on the wire.)
835 /// </remarks>
836 internal static int DecodeZigZag32(uint n)
837 {
838 return (int)(n >> 1) ^ -(int)(n & 1);
839 }
840
841 /// <summary>
842 /// Decode a 32-bit value with ZigZag encoding.
843 /// </summary>
844 /// <remarks>
845 /// ZigZag encodes signed integers into values that can be efficiently
846 /// encoded with varint. (Otherwise, negative values must be
847 /// sign-extended to 64 bits to be varint encoded, thus always taking
848 /// 10 bytes on the wire.)
849 /// </remarks>
850 internal static long DecodeZigZag64(ulong n)
851 {
852 return (long)(n >> 1) ^ -(long)(n & 1);
853 }
csharptest71f662c2011-05-20 15:15:34 -0500854 #endregion
855
csharptest71f662c2011-05-20 15:15:34 -0500856 #region Internal reading and buffer management
857
858 /// <summary>
csharptest71f662c2011-05-20 15:15:34 -0500859 /// Sets currentLimit to (current position) + byteLimit. This is called
860 /// when descending into a length-delimited embedded message. The previous
861 /// limit is returned.
862 /// </summary>
863 /// <returns>The old limit.</returns>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100864 internal int PushLimit(int byteLimit)
csharptest71f662c2011-05-20 15:15:34 -0500865 {
866 if (byteLimit < 0)
867 {
868 throw InvalidProtocolBufferException.NegativeSize();
869 }
870 byteLimit += totalBytesRetired + bufferPos;
871 int oldLimit = currentLimit;
872 if (byteLimit > oldLimit)
873 {
874 throw InvalidProtocolBufferException.TruncatedMessage();
875 }
876 currentLimit = byteLimit;
877
878 RecomputeBufferSizeAfterLimit();
879
880 return oldLimit;
881 }
882
883 private void RecomputeBufferSizeAfterLimit()
884 {
885 bufferSize += bufferSizeAfterLimit;
886 int bufferEnd = totalBytesRetired + bufferSize;
887 if (bufferEnd > currentLimit)
888 {
889 // Limit is in current buffer.
890 bufferSizeAfterLimit = bufferEnd - currentLimit;
891 bufferSize -= bufferSizeAfterLimit;
892 }
893 else
894 {
895 bufferSizeAfterLimit = 0;
896 }
897 }
898
899 /// <summary>
900 /// Discards the current limit, returning the previous limit.
901 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100902 internal void PopLimit(int oldLimit)
csharptest71f662c2011-05-20 15:15:34 -0500903 {
904 currentLimit = oldLimit;
905 RecomputeBufferSizeAfterLimit();
906 }
907
908 /// <summary>
909 /// Returns whether or not all the data before the limit has been read.
910 /// </summary>
911 /// <returns></returns>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100912 internal bool ReachedLimit
csharptest71f662c2011-05-20 15:15:34 -0500913 {
914 get
915 {
916 if (currentLimit == int.MaxValue)
917 {
918 return false;
919 }
920 int currentAbsolutePosition = totalBytesRetired + bufferPos;
921 return currentAbsolutePosition >= currentLimit;
922 }
923 }
924
925 /// <summary>
926 /// Returns true if the stream has reached the end of the input. This is the
927 /// case if either the end of the underlying input source has been reached or
928 /// the stream has reached a limit created using PushLimit.
929 /// </summary>
930 public bool IsAtEnd
931 {
932 get { return bufferPos == bufferSize && !RefillBuffer(false); }
933 }
934
935 /// <summary>
936 /// Called when buffer is empty to read more bytes from the
937 /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
938 /// either there will be at least one byte in the buffer when it returns
939 /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
940 /// RefillBuffer() returns false if no more bytes were available.
941 /// </summary>
942 /// <param name="mustSucceed"></param>
943 /// <returns></returns>
944 private bool RefillBuffer(bool mustSucceed)
945 {
946 if (bufferPos < bufferSize)
947 {
948 throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
949 }
950
951 if (totalBytesRetired + bufferSize == currentLimit)
952 {
953 // Oops, we hit a limit.
954 if (mustSucceed)
955 {
956 throw InvalidProtocolBufferException.TruncatedMessage();
957 }
958 else
959 {
960 return false;
961 }
962 }
963
964 totalBytesRetired += bufferSize;
965
966 bufferPos = 0;
967 bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
968 if (bufferSize < 0)
969 {
970 throw new InvalidOperationException("Stream.Read returned a negative count");
971 }
972 if (bufferSize == 0)
973 {
974 if (mustSucceed)
975 {
976 throw InvalidProtocolBufferException.TruncatedMessage();
977 }
978 else
979 {
980 return false;
981 }
982 }
983 else
984 {
985 RecomputeBufferSizeAfterLimit();
986 int totalBytesRead =
987 totalBytesRetired + bufferSize + bufferSizeAfterLimit;
988 if (totalBytesRead > sizeLimit || totalBytesRead < 0)
989 {
990 throw InvalidProtocolBufferException.SizeLimitExceeded();
991 }
992 return true;
993 }
994 }
995
996 /// <summary>
997 /// Read one byte from the input.
998 /// </summary>
999 /// <exception cref="InvalidProtocolBufferException">
1000 /// the end of the stream or the current limit was reached
1001 /// </exception>
Jon Skeetf34d37a2015-06-30 13:16:20 +01001002 internal byte ReadRawByte()
csharptest71f662c2011-05-20 15:15:34 -05001003 {
1004 if (bufferPos == bufferSize)
1005 {
1006 RefillBuffer(true);
1007 }
1008 return buffer[bufferPos++];
1009 }
1010
1011 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +01001012 /// Reads a fixed size of bytes from the input.
csharptest71f662c2011-05-20 15:15:34 -05001013 /// </summary>
1014 /// <exception cref="InvalidProtocolBufferException">
1015 /// the end of the stream or the current limit was reached
1016 /// </exception>
Jon Skeetf34d37a2015-06-30 13:16:20 +01001017 internal byte[] ReadRawBytes(int size)
csharptest71f662c2011-05-20 15:15:34 -05001018 {
1019 if (size < 0)
1020 {
1021 throw InvalidProtocolBufferException.NegativeSize();
1022 }
1023
1024 if (totalBytesRetired + bufferPos + size > currentLimit)
1025 {
Jon Skeetf34d37a2015-06-30 13:16:20 +01001026 // Read to the end of the stream (up to the current limit) anyway.
csharptest71f662c2011-05-20 15:15:34 -05001027 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1028 // Then fail.
1029 throw InvalidProtocolBufferException.TruncatedMessage();
1030 }
1031
1032 if (size <= bufferSize - bufferPos)
1033 {
1034 // We have all the bytes we need already.
1035 byte[] bytes = new byte[size];
csharptestaef072a2011-06-08 18:00:43 -05001036 ByteArray.Copy(buffer, bufferPos, bytes, 0, size);
csharptest71f662c2011-05-20 15:15:34 -05001037 bufferPos += size;
1038 return bytes;
1039 }
csharptest92fcf352013-05-07 15:56:50 -05001040 else if (size < buffer.Length)
csharptest71f662c2011-05-20 15:15:34 -05001041 {
1042 // Reading more bytes than are in the buffer, but not an excessive number
1043 // of bytes. We can safely allocate the resulting array ahead of time.
1044
1045 // First copy what we have.
1046 byte[] bytes = new byte[size];
1047 int pos = bufferSize - bufferPos;
csharptestaef072a2011-06-08 18:00:43 -05001048 ByteArray.Copy(buffer, bufferPos, bytes, 0, pos);
csharptest71f662c2011-05-20 15:15:34 -05001049 bufferPos = bufferSize;
1050
1051 // We want to use RefillBuffer() and then copy from the buffer into our
1052 // byte array rather than reading directly into our byte array because
1053 // the input may be unbuffered.
1054 RefillBuffer(true);
1055
1056 while (size - pos > bufferSize)
1057 {
csharptest2772dfe2011-06-08 15:50:58 -05001058 Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize);
csharptest71f662c2011-05-20 15:15:34 -05001059 pos += bufferSize;
1060 bufferPos = bufferSize;
1061 RefillBuffer(true);
1062 }
1063
csharptestaef072a2011-06-08 18:00:43 -05001064 ByteArray.Copy(buffer, 0, bytes, pos, size - pos);
csharptest71f662c2011-05-20 15:15:34 -05001065 bufferPos = size - pos;
1066
1067 return bytes;
1068 }
1069 else
1070 {
1071 // The size is very large. For security reasons, we can't allocate the
1072 // entire byte array yet. The size comes directly from the input, so a
1073 // maliciously-crafted message could provide a bogus very large size in
1074 // order to trick the app into allocating a lot of memory. We avoid this
1075 // by allocating and reading only a small chunk at a time, so that the
1076 // malicious message must actually *be* extremely large to cause
1077 // problems. Meanwhile, we limit the allowed size of a message elsewhere.
1078
1079 // Remember the buffer markers since we'll have to copy the bytes out of
1080 // it later.
1081 int originalBufferPos = bufferPos;
1082 int originalBufferSize = bufferSize;
1083
1084 // Mark the current buffer consumed.
1085 totalBytesRetired += bufferSize;
1086 bufferPos = 0;
1087 bufferSize = 0;
1088
1089 // Read all the rest of the bytes we need.
1090 int sizeLeft = size - (originalBufferSize - originalBufferPos);
1091 List<byte[]> chunks = new List<byte[]>();
1092
1093 while (sizeLeft > 0)
1094 {
csharptest92fcf352013-05-07 15:56:50 -05001095 byte[] chunk = new byte[Math.Min(sizeLeft, buffer.Length)];
csharptest71f662c2011-05-20 15:15:34 -05001096 int pos = 0;
1097 while (pos < chunk.Length)
1098 {
1099 int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
1100 if (n <= 0)
1101 {
1102 throw InvalidProtocolBufferException.TruncatedMessage();
1103 }
1104 totalBytesRetired += n;
1105 pos += n;
1106 }
1107 sizeLeft -= chunk.Length;
1108 chunks.Add(chunk);
1109 }
1110
1111 // OK, got everything. Now concatenate it all into one buffer.
1112 byte[] bytes = new byte[size];
1113
1114 // Start by copying the leftover bytes from this.buffer.
1115 int newPos = originalBufferSize - originalBufferPos;
csharptestaef072a2011-06-08 18:00:43 -05001116 ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos);
csharptest71f662c2011-05-20 15:15:34 -05001117
1118 // And now all the chunks.
1119 foreach (byte[] chunk in chunks)
1120 {
csharptest2772dfe2011-06-08 15:50:58 -05001121 Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length);
csharptest71f662c2011-05-20 15:15:34 -05001122 newPos += chunk.Length;
1123 }
1124
1125 // Done.
1126 return bytes;
1127 }
1128 }
1129
1130 /// <summary>
csharptest71f662c2011-05-20 15:15:34 -05001131 /// Reads and discards <paramref name="size"/> bytes.
1132 /// </summary>
1133 /// <exception cref="InvalidProtocolBufferException">the end of the stream
1134 /// or the current limit was reached</exception>
Jon Skeetf34d37a2015-06-30 13:16:20 +01001135 private void SkipRawBytes(int size)
csharptest71f662c2011-05-20 15:15:34 -05001136 {
1137 if (size < 0)
1138 {
1139 throw InvalidProtocolBufferException.NegativeSize();
1140 }
1141
1142 if (totalBytesRetired + bufferPos + size > currentLimit)
1143 {
1144 // Read to the end of the stream anyway.
1145 SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
1146 // Then fail.
1147 throw InvalidProtocolBufferException.TruncatedMessage();
1148 }
1149
1150 if (size <= bufferSize - bufferPos)
1151 {
1152 // We have all the bytes we need already.
1153 bufferPos += size;
1154 }
1155 else
1156 {
1157 // Skipping more bytes than are in the buffer. First skip what we have.
1158 int pos = bufferSize - bufferPos;
csharptestef7091c2013-05-07 15:00:50 -05001159
1160 // ROK 5/7/2013 Issue #54: should retire all bytes in buffer (bufferSize)
1161 // totalBytesRetired += pos;
1162 totalBytesRetired += bufferSize;
1163
csharptest71f662c2011-05-20 15:15:34 -05001164 bufferPos = 0;
1165 bufferSize = 0;
1166
1167 // Then skip directly from the InputStream for the rest.
1168 if (pos < size)
1169 {
1170 if (input == null)
1171 {
1172 throw InvalidProtocolBufferException.TruncatedMessage();
1173 }
1174 SkipImpl(size - pos);
1175 totalBytesRetired += size - pos;
1176 }
1177 }
1178 }
1179
1180 /// <summary>
1181 /// Abstraction of skipping to cope with streams which can't really skip.
1182 /// </summary>
1183 private void SkipImpl(int amountToSkip)
1184 {
1185 if (input.CanSeek)
1186 {
1187 long previousPosition = input.Position;
1188 input.Position += amountToSkip;
1189 if (input.Position != previousPosition + amountToSkip)
1190 {
1191 throw InvalidProtocolBufferException.TruncatedMessage();
1192 }
1193 }
1194 else
1195 {
Jon Skeetca2adbd2015-06-12 09:57:04 +01001196 byte[] skipBuffer = new byte[Math.Min(1024, amountToSkip)];
csharptest71f662c2011-05-20 15:15:34 -05001197 while (amountToSkip > 0)
1198 {
Jon Skeetca2adbd2015-06-12 09:57:04 +01001199 int bytesRead = input.Read(skipBuffer, 0, Math.Min(skipBuffer.Length, amountToSkip));
csharptest71f662c2011-05-20 15:15:34 -05001200 if (bytesRead <= 0)
1201 {
1202 throw InvalidProtocolBufferException.TruncatedMessage();
1203 }
1204 amountToSkip -= bytesRead;
1205 }
1206 }
1207 }
1208
1209 #endregion
1210 }
1211}