| 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 |