OLD | NEW |
1 #region Copyright notice and license | 1 #region Copyright notice and license |
2 // Protocol Buffers - Google's data interchange format | 2 // Protocol Buffers - Google's data interchange format |
3 // Copyright 2008 Google Inc. All rights reserved. | 3 // Copyright 2008 Google Inc. All rights reserved. |
4 // https://developers.google.com/protocol-buffers/ | 4 // https://developers.google.com/protocol-buffers/ |
5 // | 5 // |
6 // Redistribution and use in source and binary forms, with or without | 6 // Redistribution and use in source and binary forms, with or without |
7 // modification, are permitted provided that the following conditions are | 7 // modification, are permitted provided that the following conditions are |
8 // met: | 8 // met: |
9 // | 9 // |
10 // * Redistributions of source code must retain the above copyright | 10 // * Redistributions of source code must retain the above copyright |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
44 /// <para> | 44 /// <para> |
45 /// This class is generally used by generated code to read appropriate | 45 /// This class is generally used by generated code to read appropriate |
46 /// primitives from the stream. It effectively encapsulates the lowest | 46 /// primitives from the stream. It effectively encapsulates the lowest |
47 /// levels of protocol buffer format. | 47 /// levels of protocol buffer format. |
48 /// </para> | 48 /// </para> |
49 /// <para> | 49 /// <para> |
50 /// Repeated fields and map fields are not handled by this class; use <see c
ref="RepeatedField{T}"/> | 50 /// Repeated fields and map fields are not handled by this class; use <see c
ref="RepeatedField{T}"/> |
51 /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields. | 51 /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields. |
52 /// </para> | 52 /// </para> |
53 /// </remarks> | 53 /// </remarks> |
54 public sealed class CodedInputStream | 54 public sealed class CodedInputStream : IDisposable |
55 { | 55 { |
56 /// <summary> | 56 /// <summary> |
| 57 /// Whether to leave the underlying stream open when disposing of this s
tream. |
| 58 /// This is always true when there's no stream. |
| 59 /// </summary> |
| 60 private readonly bool leaveOpen; |
| 61 |
| 62 /// <summary> |
57 /// Buffer of data read from the stream or provided at construction time
. | 63 /// Buffer of data read from the stream or provided at construction time
. |
58 /// </summary> | 64 /// </summary> |
59 private readonly byte[] buffer; | 65 private readonly byte[] buffer; |
60 | 66 |
61 /// <summary> | 67 /// <summary> |
62 /// The index of the buffer at which we need to refill from the stream (
if there is one). | 68 /// The index of the buffer at which we need to refill from the stream (
if there is one). |
63 /// </summary> | 69 /// </summary> |
64 private int bufferSize; | 70 private int bufferSize; |
65 | 71 |
66 private int bufferSizeAfterLimit = 0; | 72 private int bufferSizeAfterLimit = 0; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
108 private readonly int recursionLimit; | 114 private readonly int recursionLimit; |
109 private readonly int sizeLimit; | 115 private readonly int sizeLimit; |
110 | 116 |
111 #region Construction | 117 #region Construction |
112 // Note that the checks are performed such that we don't end up checking
obviously-valid things | 118 // Note that the checks are performed such that we don't end up checking
obviously-valid things |
113 // like non-null references for arrays we've just created. | 119 // like non-null references for arrays we've just created. |
114 | 120 |
115 /// <summary> | 121 /// <summary> |
116 /// Creates a new CodedInputStream reading data from the given byte arra
y. | 122 /// Creates a new CodedInputStream reading data from the given byte arra
y. |
117 /// </summary> | 123 /// </summary> |
118 public CodedInputStream(byte[] buffer) : this(null, Preconditions.CheckN
otNull(buffer, "buffer"), 0, buffer.Length) | 124 public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.C
heckNotNull(buffer, "buffer"), 0, buffer.Length) |
119 { | 125 { |
120 } | 126 } |
121 | 127 |
122 /// <summary> | 128 /// <summary> |
123 /// Creates a new CodedInputStream that reads from the given byte array
slice. | 129 /// Creates a new <see cref="CodedInputStream"/> that reads from the giv
en byte array slice. |
124 /// </summary> | 130 /// </summary> |
125 public CodedInputStream(byte[] buffer, int offset, int length) | 131 public CodedInputStream(byte[] buffer, int offset, int length) |
126 : this(null, Preconditions.CheckNotNull(buffer, "buffer"), offset, o
ffset + length) | 132 : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), offs
et, offset + length) |
127 { | 133 { |
128 if (offset < 0 || offset > buffer.Length) | 134 if (offset < 0 || offset > buffer.Length) |
129 { | 135 { |
130 throw new ArgumentOutOfRangeException("offset", "Offset must be
within the buffer"); | 136 throw new ArgumentOutOfRangeException("offset", "Offset must be
within the buffer"); |
131 } | 137 } |
132 if (length < 0 || offset + length > buffer.Length) | 138 if (length < 0 || offset + length > buffer.Length) |
133 { | 139 { |
134 throw new ArgumentOutOfRangeException("length", "Length must be
non-negative and within the buffer"); | 140 throw new ArgumentOutOfRangeException("length", "Length must be
non-negative and within the buffer"); |
135 } | 141 } |
136 } | 142 } |
137 | 143 |
138 /// <summary> | 144 /// <summary> |
139 /// Creates a new CodedInputStream reading data from the given stream. | 145 /// Creates a new <see cref="CodedInputStream"/> reading data from the g
iven stream, which will be disposed |
140 /// </summary> | 146 /// when the returned object is disposed. |
141 public CodedInputStream(Stream input) : this(input, new byte[BufferSize]
, 0, 0) | 147 /// </summary> |
142 { | 148 /// <param name="input">The stream to read from.</param> |
143 Preconditions.CheckNotNull(input, "input"); | 149 public CodedInputStream(Stream input) : this(input, false) |
144 } | 150 { |
145 | 151 } |
| 152 |
| 153 /// <summary> |
| 154 /// Creates a new <see cref="CodedInputStream"/> reading data from the g
iven stream. |
| 155 /// </summary> |
| 156 /// <param name="input">The stream to read from.</param> |
| 157 /// <param name="leaveOpen"><c>true</c> to leave <paramref name="input"/
> open when the returned |
| 158 /// <c cref="CodedInputStream"/> is disposed; <c>false</c> to dispose of
the given stream when the |
| 159 /// returned object is disposed.</param> |
| 160 public CodedInputStream(Stream input, bool leaveOpen) |
| 161 : this(ProtoPreconditions.CheckNotNull(input, "input"), new byte[Buf
ferSize], 0, 0) |
| 162 { |
| 163 this.leaveOpen = leaveOpen; |
| 164 } |
| 165 |
146 /// <summary> | 166 /// <summary> |
147 /// Creates a new CodedInputStream reading data from the given | 167 /// Creates a new CodedInputStream reading data from the given |
148 /// stream and buffer, using the default limits. | 168 /// stream and buffer, using the default limits. |
149 /// </summary> | 169 /// </summary> |
150 internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, in
t bufferSize) | 170 internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, in
t bufferSize) |
151 { | 171 { |
152 this.input = input; | 172 this.input = input; |
153 this.buffer = buffer; | 173 this.buffer = buffer; |
154 this.bufferPos = bufferPos; | 174 this.bufferPos = bufferPos; |
155 this.bufferSize = bufferSize; | 175 this.bufferSize = bufferSize; |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 /// to avoid maliciously-recursive data. | 259 /// to avoid maliciously-recursive data. |
240 /// </summary> | 260 /// </summary> |
241 /// <remarks> | 261 /// <remarks> |
242 /// The default limit is 64. | 262 /// The default limit is 64. |
243 /// </remarks> | 263 /// </remarks> |
244 /// <value> | 264 /// <value> |
245 /// The recursion limit for this stream. | 265 /// The recursion limit for this stream. |
246 /// </value> | 266 /// </value> |
247 public int RecursionLimit { get { return recursionLimit; } } | 267 public int RecursionLimit { get { return recursionLimit; } } |
248 | 268 |
| 269 /// <summary> |
| 270 /// Disposes of this instance, potentially closing any underlying stream
. |
| 271 /// </summary> |
| 272 /// <remarks> |
| 273 /// As there is no flushing to perform here, disposing of a <see cref="C
odedInputStream"/> which |
| 274 /// was constructed with the <c>leaveOpen</c> option parameter set to <c
>true</c> (or one which |
| 275 /// was constructed to read from a byte array) has no effect. |
| 276 /// </remarks> |
| 277 public void Dispose() |
| 278 { |
| 279 if (!leaveOpen) |
| 280 { |
| 281 input.Dispose(); |
| 282 } |
| 283 } |
| 284 |
249 #region Validation | 285 #region Validation |
250 /// <summary> | 286 /// <summary> |
251 /// Verifies that the last call to ReadTag() returned tag 0 - in other w
ords, | 287 /// Verifies that the last call to ReadTag() returned tag 0 - in other w
ords, |
252 /// we've reached the end of the stream when we expected to. | 288 /// we've reached the end of the stream when we expected to. |
253 /// </summary> | 289 /// </summary> |
254 /// <exception cref="InvalidProtocolBufferException">The | 290 /// <exception cref="InvalidProtocolBufferException">The |
255 /// tag read was not the one specified</exception> | 291 /// tag read was not the one specified</exception> |
256 internal void CheckReadEndOfStreamTag() | 292 internal void CheckReadEndOfStreamTag() |
257 { | 293 { |
258 if (lastTag != 0) | 294 if (lastTag != 0) |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 throw InvalidProtocolBufferException.InvalidTag(); | 378 throw InvalidProtocolBufferException.InvalidTag(); |
343 } | 379 } |
344 return lastTag; | 380 return lastTag; |
345 } | 381 } |
346 | 382 |
347 /// <summary> | 383 /// <summary> |
348 /// Skips the data for the field with the tag we've just read. | 384 /// Skips the data for the field with the tag we've just read. |
349 /// This should be called directly after <see cref="ReadTag"/>, when | 385 /// This should be called directly after <see cref="ReadTag"/>, when |
350 /// the caller wishes to skip an unknown field. | 386 /// the caller wishes to skip an unknown field. |
351 /// </summary> | 387 /// </summary> |
| 388 /// <remarks> |
| 389 /// This method throws <see cref="InvalidProtocolBufferException"/> if t
he last-read tag was an end-group tag. |
| 390 /// If a caller wishes to skip a group, they should skip the whole group
, by calling this method after reading the |
| 391 /// start-group tag. This behavior allows callers to call this method on
any field they don't understand, correctly |
| 392 /// resulting in an error if an end-group tag has not been paired with a
n earlier start-group tag. |
| 393 /// </remarks> |
| 394 /// <exception cref="InvalidProtocolBufferException">The last tag was an
end-group tag</exception> |
| 395 /// <exception cref="InvalidOperationException">The last read operation
read to the end of the logical stream</exception> |
352 public void SkipLastField() | 396 public void SkipLastField() |
353 { | 397 { |
354 if (lastTag == 0) | 398 if (lastTag == 0) |
355 { | 399 { |
356 throw new InvalidOperationException("SkipLastField cannot be cal
led at the end of a stream"); | 400 throw new InvalidOperationException("SkipLastField cannot be cal
led at the end of a stream"); |
357 } | 401 } |
358 switch (WireFormat.GetTagWireType(lastTag)) | 402 switch (WireFormat.GetTagWireType(lastTag)) |
359 { | 403 { |
360 case WireFormat.WireType.StartGroup: | 404 case WireFormat.WireType.StartGroup: |
361 SkipGroup(); | 405 SkipGroup(lastTag); |
362 break; | 406 break; |
363 case WireFormat.WireType.EndGroup: | 407 case WireFormat.WireType.EndGroup: |
364 // Just ignore; there's no data following the tag. | 408 throw new InvalidProtocolBufferException( |
365 break; | 409 "SkipLastField called on an end-group tag, indicating th
at the corresponding start-group was missing"); |
366 case WireFormat.WireType.Fixed32: | 410 case WireFormat.WireType.Fixed32: |
367 ReadFixed32(); | 411 ReadFixed32(); |
368 break; | 412 break; |
369 case WireFormat.WireType.Fixed64: | 413 case WireFormat.WireType.Fixed64: |
370 ReadFixed64(); | 414 ReadFixed64(); |
371 break; | 415 break; |
372 case WireFormat.WireType.LengthDelimited: | 416 case WireFormat.WireType.LengthDelimited: |
373 var length = ReadLength(); | 417 var length = ReadLength(); |
374 SkipRawBytes(length); | 418 SkipRawBytes(length); |
375 break; | 419 break; |
376 case WireFormat.WireType.Varint: | 420 case WireFormat.WireType.Varint: |
377 ReadRawVarint32(); | 421 ReadRawVarint32(); |
378 break; | 422 break; |
379 } | 423 } |
380 } | 424 } |
381 | 425 |
382 private void SkipGroup() | 426 private void SkipGroup(uint startGroupTag) |
383 { | 427 { |
384 // Note: Currently we expect this to be the way that groups are read
. We could put the recursion | 428 // Note: Currently we expect this to be the way that groups are read
. We could put the recursion |
385 // depth changes into the ReadTag method instead, potentially... | 429 // depth changes into the ReadTag method instead, potentially... |
386 recursionDepth++; | 430 recursionDepth++; |
387 if (recursionDepth >= recursionLimit) | 431 if (recursionDepth >= recursionLimit) |
388 { | 432 { |
389 throw InvalidProtocolBufferException.RecursionLimitExceeded(); | 433 throw InvalidProtocolBufferException.RecursionLimitExceeded(); |
390 } | 434 } |
391 uint tag; | 435 uint tag; |
392 do | 436 while (true) |
393 { | 437 { |
394 tag = ReadTag(); | 438 tag = ReadTag(); |
395 if (tag == 0) | 439 if (tag == 0) |
396 { | 440 { |
397 throw InvalidProtocolBufferException.TruncatedMessage(); | 441 throw InvalidProtocolBufferException.TruncatedMessage(); |
398 } | 442 } |
| 443 // Can't call SkipLastField for this case- that would throw. |
| 444 if (WireFormat.GetTagWireType(tag) == WireFormat.WireType.EndGro
up) |
| 445 { |
| 446 break; |
| 447 } |
399 // This recursion will allow us to handle nested groups. | 448 // This recursion will allow us to handle nested groups. |
400 SkipLastField(); | 449 SkipLastField(); |
401 } while (WireFormat.GetTagWireType(tag) != WireFormat.WireType.EndGr
oup); | 450 } |
| 451 int startField = WireFormat.GetTagFieldNumber(startGroupTag); |
| 452 int endField = WireFormat.GetTagFieldNumber(tag); |
| 453 if (startField != endField) |
| 454 { |
| 455 throw new InvalidProtocolBufferException( |
| 456 $"Mismatched end-group tag. Started with field {startField};
ended with field {endField}"); |
| 457 } |
402 recursionDepth--; | 458 recursionDepth--; |
403 } | 459 } |
404 | 460 |
405 /// <summary> | 461 /// <summary> |
406 /// Reads a double field from the stream. | 462 /// Reads a double field from the stream. |
407 /// </summary> | 463 /// </summary> |
408 public double ReadDouble() | 464 public double ReadDouble() |
409 { | 465 { |
410 return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64()
); | 466 return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64()
); |
411 } | 467 } |
(...skipping 800 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1212 throw InvalidProtocolBufferException.TruncatedMessage(); | 1268 throw InvalidProtocolBufferException.TruncatedMessage(); |
1213 } | 1269 } |
1214 amountToSkip -= bytesRead; | 1270 amountToSkip -= bytesRead; |
1215 } | 1271 } |
1216 } | 1272 } |
1217 } | 1273 } |
1218 | 1274 |
1219 #endregion | 1275 #endregion |
1220 } | 1276 } |
1221 } | 1277 } |
OLD | NEW |