CodedInputStream.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // http://github.com/jskeet/dotnet-protobufs/
  4. // Original C++/Java/Python code:
  5. // http://code.google.com/p/protobuf/
  6. //
  7. // Redistribution and use in source and binary forms, with or without
  8. // modification, are permitted provided that the following conditions are
  9. // met:
  10. //
  11. // * Redistributions of source code must retain the above copyright
  12. // notice, this list of conditions and the following disclaimer.
  13. // * Redistributions in binary form must reproduce the above
  14. // copyright notice, this list of conditions and the following disclaimer
  15. // in the documentation and/or other materials provided with the
  16. // distribution.
  17. // * Neither the name of Google Inc. nor the names of its
  18. // contributors may be used to endorse or promote products derived from
  19. // this software without specific prior written permission.
  20. //
  21. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. using System;
  33. using System.Collections.Generic;
  34. using System.IO;
  35. using System.Text;
  36. using Google.ProtocolBuffers.Descriptors;
  37. namespace Google.ProtocolBuffers {
  38. /// <summary>
  39. /// Readings and decodes protocol message fields.
  40. /// </summary>
  41. /// <remarks>
  42. /// This class contains two kinds of methods: methods that read specific
  43. /// protocol message constructs and field types (e.g. ReadTag and
  44. /// ReadInt32) and methods that read low-level values (e.g.
  45. /// ReadRawVarint32 and ReadRawBytes). If you are reading encoded protocol
  46. /// messages, you should use the former methods, but if you are reading some
  47. /// other format of your own design, use the latter. The names of the former
  48. /// methods are taken from the protocol buffer type names, not .NET types.
  49. /// (Hence ReadFloat instead of ReadSingle, and ReadBool instead of ReadBoolean.)
  50. ///
  51. /// TODO(jonskeet): Consider whether recursion and size limits shouldn't be readonly,
  52. /// set at construction time.
  53. /// </remarks>
  54. public sealed class CodedInputStream {
  55. private readonly byte[] buffer;
  56. private int bufferSize;
  57. private int bufferSizeAfterLimit = 0;
  58. private int bufferPos = 0;
  59. private readonly Stream input;
  60. private uint lastTag = 0;
  61. const int DefaultRecursionLimit = 64;
  62. const int DefaultSizeLimit = 64 << 20; // 64MB
  63. const int BufferSize = 4096;
  64. /// <summary>
  65. /// The total number of bytes read before the current buffer. The
  66. /// total bytes read up to the current position can be computed as
  67. /// totalBytesRetired + bufferPos.
  68. /// </summary>
  69. private int totalBytesRetired = 0;
  70. /// <summary>
  71. /// The absolute position of the end of the current message.
  72. /// </summary>
  73. private int currentLimit = int.MaxValue;
  74. /// <summary>
  75. /// <see cref="SetRecursionLimit"/>
  76. /// </summary>
  77. private int recursionDepth = 0;
  78. private int recursionLimit = DefaultRecursionLimit;
  79. /// <summary>
  80. /// <see cref="SetSizeLimit"/>
  81. /// </summary>
  82. private int sizeLimit = DefaultSizeLimit;
  83. #region Construction
  84. /// <summary>
  85. /// Creates a new CodedInputStream reading data from the given
  86. /// stream.
  87. /// </summary>
  88. public static CodedInputStream CreateInstance(Stream input) {
  89. return new CodedInputStream(input);
  90. }
  91. /// <summary>
  92. /// Creates a new CodedInputStream reading data from the given
  93. /// byte array.
  94. /// </summary>
  95. public static CodedInputStream CreateInstance(byte[] buf) {
  96. return new CodedInputStream(buf);
  97. }
  98. private CodedInputStream(byte[] buffer) {
  99. this.buffer = buffer;
  100. this.bufferSize = buffer.Length;
  101. this.input = null;
  102. }
  103. private CodedInputStream(Stream input) {
  104. this.buffer = new byte[BufferSize];
  105. this.bufferSize = 0;
  106. this.input = input;
  107. }
  108. #endregion
  109. #region Validation
  110. /// <summary>
  111. /// Verifies that the last call to ReadTag() returned the given tag value.
  112. /// This is used to verify that a nested group ended with the correct
  113. /// end tag.
  114. /// </summary>
  115. /// <exception cref="InvalidProtocolBufferException">The last
  116. /// tag read was not the one specified</exception>
  117. public void CheckLastTagWas(uint value) {
  118. if (lastTag != value) {
  119. throw InvalidProtocolBufferException.InvalidEndTag();
  120. }
  121. }
  122. #endregion
  123. #region Reading of tags etc
  124. /// <summary>
  125. /// Attempt to read a field tag, returning 0 if we have reached the end
  126. /// of the input data. Protocol message parsers use this to read tags,
  127. /// since a protocol message may legally end wherever a tag occurs, and
  128. /// zero is not a valid tag number.
  129. /// </summary>
  130. public uint ReadTag() {
  131. if (bufferPos == bufferSize && !RefillBuffer(false)) {
  132. lastTag = 0;
  133. return 0;
  134. }
  135. lastTag = ReadRawVarint32();
  136. if (lastTag == 0) {
  137. // If we actually read zero, that's not a valid tag.
  138. throw InvalidProtocolBufferException.InvalidTag();
  139. }
  140. return lastTag;
  141. }
  142. /// <summary>
  143. /// Read a double field from the stream.
  144. /// </summary>
  145. public double ReadDouble() {
  146. // TODO(jonskeet): Test this on different endiannesses
  147. return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
  148. }
  149. /// <summary>
  150. /// Read a float field from the stream.
  151. /// </summary>
  152. public float ReadFloat() {
  153. // TODO(jonskeet): Test this on different endiannesses
  154. uint raw = ReadRawLittleEndian32();
  155. byte[] rawBytes = BitConverter.GetBytes(raw);
  156. return BitConverter.ToSingle(rawBytes, 0);
  157. }
  158. /// <summary>
  159. /// Read a uint64 field from the stream.
  160. /// </summary>
  161. public ulong ReadUInt64() {
  162. return ReadRawVarint64();
  163. }
  164. /// <summary>
  165. /// Read an int64 field from the stream.
  166. /// </summary>
  167. public long ReadInt64() {
  168. return (long) ReadRawVarint64();
  169. }
  170. /// <summary>
  171. /// Read an int32 field from the stream.
  172. /// </summary>
  173. public int ReadInt32() {
  174. return (int) ReadRawVarint32();
  175. }
  176. /// <summary>
  177. /// Read a fixed64 field from the stream.
  178. /// </summary>
  179. public ulong ReadFixed64() {
  180. return ReadRawLittleEndian64();
  181. }
  182. /// <summary>
  183. /// Read a fixed32 field from the stream.
  184. /// </summary>
  185. public uint ReadFixed32() {
  186. return ReadRawLittleEndian32();
  187. }
  188. /// <summary>
  189. /// Read a bool field from the stream.
  190. /// </summary>
  191. public bool ReadBool() {
  192. return ReadRawVarint32() != 0;
  193. }
  194. /// <summary>
  195. /// Reads a string field from the stream.
  196. /// </summary>
  197. public String ReadString() {
  198. int size = (int) ReadRawVarint32();
  199. // No need to read any data for an empty string.
  200. if (size == 0) {
  201. return "";
  202. }
  203. if (size <= bufferSize - bufferPos) {
  204. // Fast path: We already have the bytes in a contiguous buffer, so
  205. // just copy directly from it.
  206. String result = Encoding.UTF8.GetString(buffer, bufferPos, size);
  207. bufferPos += size;
  208. return result;
  209. }
  210. // Slow path: Build a byte array first then copy it.
  211. return Encoding.UTF8.GetString(ReadRawBytes(size));
  212. }
  213. /// <summary>
  214. /// Reads a group field value from the stream.
  215. /// </summary>
  216. public void ReadGroup(int fieldNumber, IBuilder builder,
  217. ExtensionRegistry extensionRegistry) {
  218. if (recursionDepth >= recursionLimit) {
  219. throw InvalidProtocolBufferException.RecursionLimitExceeded();
  220. }
  221. ++recursionDepth;
  222. builder.WeakMergeFrom(this, extensionRegistry);
  223. CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
  224. --recursionDepth;
  225. }
  226. /// <summary>
  227. /// Reads a group field value from the stream and merges it into the given
  228. /// UnknownFieldSet.
  229. /// </summary>
  230. public void ReadUnknownGroup(int fieldNumber, UnknownFieldSet.Builder builder) {
  231. if (recursionDepth >= recursionLimit) {
  232. throw InvalidProtocolBufferException.RecursionLimitExceeded();
  233. }
  234. ++recursionDepth;
  235. builder.MergeFrom(this);
  236. CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
  237. --recursionDepth;
  238. }
  239. /// <summary>
  240. /// Reads an embedded message field value from the stream.
  241. /// </summary>
  242. public void ReadMessage(IBuilder builder, ExtensionRegistry extensionRegistry) {
  243. int length = (int) ReadRawVarint32();
  244. if (recursionDepth >= recursionLimit) {
  245. throw InvalidProtocolBufferException.RecursionLimitExceeded();
  246. }
  247. int oldLimit = PushLimit(length);
  248. ++recursionDepth;
  249. builder.WeakMergeFrom(this, extensionRegistry);
  250. CheckLastTagWas(0);
  251. --recursionDepth;
  252. PopLimit(oldLimit);
  253. }
  254. /// <summary>
  255. /// Reads a bytes field value from the stream.
  256. /// </summary>
  257. public ByteString ReadBytes() {
  258. int size = (int) ReadRawVarint32();
  259. if (size < bufferSize - bufferPos && size > 0) {
  260. // Fast path: We already have the bytes in a contiguous buffer, so
  261. // just copy directly from it.
  262. ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
  263. bufferPos += size;
  264. return result;
  265. } else {
  266. // Slow path: Build a byte array first then copy it.
  267. return ByteString.CopyFrom(ReadRawBytes(size));
  268. }
  269. }
  270. /// <summary>
  271. /// Reads a uint32 field value from the stream.
  272. /// </summary>
  273. public uint ReadUInt32() {
  274. return ReadRawVarint32();
  275. }
  276. /// <summary>
  277. /// Reads an enum field value from the stream. The caller is responsible
  278. /// for converting the numeric value to an actual enum.
  279. /// </summary>
  280. public int ReadEnum() {
  281. return (int) ReadRawVarint32();
  282. }
  283. /// <summary>
  284. /// Reads an sfixed32 field value from the stream.
  285. /// </summary>
  286. public int ReadSFixed32() {
  287. return (int) ReadRawLittleEndian32();
  288. }
  289. /// <summary>
  290. /// Reads an sfixed64 field value from the stream.
  291. /// </summary>
  292. public long ReadSFixed64() {
  293. return (long) ReadRawLittleEndian64();
  294. }
  295. /// <summary>
  296. /// Reads an sint32 field value from the stream.
  297. /// </summary>
  298. public int ReadSInt32() {
  299. return DecodeZigZag32(ReadRawVarint32());
  300. }
  301. /// <summary>
  302. /// Reads an sint64 field value from the stream.
  303. /// </summary>
  304. public long ReadSInt64() {
  305. return DecodeZigZag64(ReadRawVarint64());
  306. }
  307. /// <summary>
  308. /// Reads a field of any primitive type. Enums, groups and embedded
  309. /// messages are not handled by this method.
  310. /// </summary>
  311. public object ReadPrimitiveField(FieldType fieldType) {
  312. switch (fieldType) {
  313. case FieldType.Double: return ReadDouble();
  314. case FieldType.Float: return ReadFloat();
  315. case FieldType.Int64: return ReadInt64();
  316. case FieldType.UInt64: return ReadUInt64();
  317. case FieldType.Int32: return ReadInt32();
  318. case FieldType.Fixed64: return ReadFixed64();
  319. case FieldType.Fixed32: return ReadFixed32();
  320. case FieldType.Bool: return ReadBool();
  321. case FieldType.String: return ReadString();
  322. case FieldType.Bytes: return ReadBytes();
  323. case FieldType.UInt32: return ReadUInt32();
  324. case FieldType.SFixed32: return ReadSFixed32();
  325. case FieldType.SFixed64: return ReadSFixed64();
  326. case FieldType.SInt32: return ReadSInt32();
  327. case FieldType.SInt64: return ReadSInt64();
  328. case FieldType.Group:
  329. throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
  330. case FieldType.Message:
  331. throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
  332. // We don't handle enums because we don't know what to do if the
  333. // value is not recognized.
  334. case FieldType.Enum:
  335. throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
  336. default:
  337. throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
  338. }
  339. }
  340. #endregion
  341. #region Underlying reading primitives
  342. /// <summary>
  343. /// Same code as ReadRawVarint32, but read each byte individually, checking for
  344. /// buffer overflow.
  345. /// </summary>
  346. private uint SlowReadRawVarint32() {
  347. int tmp = ReadRawByte();
  348. if (tmp < 128) {
  349. return (uint)tmp;
  350. }
  351. int result = tmp & 0x7f;
  352. if ((tmp = ReadRawByte()) < 128) {
  353. result |= tmp << 7;
  354. } else {
  355. result |= (tmp & 0x7f) << 7;
  356. if ((tmp = ReadRawByte()) < 128) {
  357. result |= tmp << 14;
  358. } else {
  359. result |= (tmp & 0x7f) << 14;
  360. if ((tmp = ReadRawByte()) < 128) {
  361. result |= tmp << 21;
  362. } else {
  363. result |= (tmp & 0x7f) << 21;
  364. result |= (tmp = ReadRawByte()) << 28;
  365. if (tmp >= 128) {
  366. // Discard upper 32 bits.
  367. for (int i = 0; i < 5; i++) {
  368. if (ReadRawByte() < 128) return (uint)result;
  369. }
  370. throw InvalidProtocolBufferException.MalformedVarint();
  371. }
  372. }
  373. }
  374. }
  375. return (uint)result;
  376. }
  377. /// <summary>
  378. /// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
  379. /// This method is optimised for the case where we've got lots of data in the buffer.
  380. /// That means we can check the size just once, then just read directly from the buffer
  381. /// without constant rechecking of the buffer length.
  382. /// </summary>
  383. public uint ReadRawVarint32() {
  384. if (bufferPos + 5 > bufferSize) {
  385. return SlowReadRawVarint32();
  386. }
  387. int tmp = buffer[bufferPos++];
  388. if (tmp < 128) {
  389. return (uint)tmp;
  390. }
  391. int result = tmp & 0x7f;
  392. if ((tmp = buffer[bufferPos++]) < 128) {
  393. result |= tmp << 7;
  394. } else {
  395. result |= (tmp & 0x7f) << 7;
  396. if ((tmp = buffer[bufferPos++]) < 128) {
  397. result |= tmp << 14;
  398. } else {
  399. result |= (tmp & 0x7f) << 14;
  400. if ((tmp = buffer[bufferPos++]) < 128) {
  401. result |= tmp << 21;
  402. } else {
  403. result |= (tmp & 0x7f) << 21;
  404. result |= (tmp = buffer[bufferPos++]) << 28;
  405. if (tmp >= 128) {
  406. // Discard upper 32 bits.
  407. // Note that this has to use ReadRawByte() as we only ensure we've
  408. // got at least 5 bytes at the start of the method. This lets us
  409. // use the fast path in more cases, and we rarely hit this section of code.
  410. for (int i = 0; i < 5; i++) {
  411. if (ReadRawByte() < 128) return (uint)result;
  412. }
  413. throw InvalidProtocolBufferException.MalformedVarint();
  414. }
  415. }
  416. }
  417. }
  418. return (uint)result;
  419. }
  420. /// <summary>
  421. /// Read a raw varint from the stream.
  422. /// </summary>
  423. public ulong ReadRawVarint64() {
  424. int shift = 0;
  425. ulong result = 0;
  426. while (shift < 64) {
  427. byte b = ReadRawByte();
  428. result |= (ulong)(b & 0x7F) << shift;
  429. if ((b & 0x80) == 0) {
  430. return result;
  431. }
  432. shift += 7;
  433. }
  434. throw InvalidProtocolBufferException.MalformedVarint();
  435. }
  436. /// <summary>
  437. /// Read a 32-bit little-endian integer from the stream.
  438. /// </summary>
  439. public uint ReadRawLittleEndian32() {
  440. uint b1 = ReadRawByte();
  441. uint b2 = ReadRawByte();
  442. uint b3 = ReadRawByte();
  443. uint b4 = ReadRawByte();
  444. return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
  445. }
  446. /// <summary>
  447. /// Read a 64-bit little-endian integer from the stream.
  448. /// </summary>
  449. public ulong ReadRawLittleEndian64() {
  450. ulong b1 = ReadRawByte();
  451. ulong b2 = ReadRawByte();
  452. ulong b3 = ReadRawByte();
  453. ulong b4 = ReadRawByte();
  454. ulong b5 = ReadRawByte();
  455. ulong b6 = ReadRawByte();
  456. ulong b7 = ReadRawByte();
  457. ulong b8 = ReadRawByte();
  458. return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
  459. | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
  460. }
  461. #endregion
  462. /// <summary>
  463. /// Decode a 32-bit value with ZigZag encoding.
  464. /// </summary>
  465. /// <remarks>
  466. /// ZigZag encodes signed integers into values that can be efficiently
  467. /// encoded with varint. (Otherwise, negative values must be
  468. /// sign-extended to 64 bits to be varint encoded, thus always taking
  469. /// 10 bytes on the wire.)
  470. /// </remarks>
  471. public static int DecodeZigZag32(uint n) {
  472. return (int)(n >> 1) ^ -(int)(n & 1);
  473. }
  474. /// <summary>
  475. /// Decode a 32-bit value with ZigZag encoding.
  476. /// </summary>
  477. /// <remarks>
  478. /// ZigZag encodes signed integers into values that can be efficiently
  479. /// encoded with varint. (Otherwise, negative values must be
  480. /// sign-extended to 64 bits to be varint encoded, thus always taking
  481. /// 10 bytes on the wire.)
  482. /// </remarks>
  483. public static long DecodeZigZag64(ulong n) {
  484. return (long)(n >> 1) ^ -(long)(n & 1);
  485. }
  486. /// <summary>
  487. /// Set the maximum message recursion depth.
  488. /// </summary>
  489. /// <remarks>
  490. /// In order to prevent malicious
  491. /// messages from causing stack overflows, CodedInputStream limits
  492. /// how deeply messages may be nested. The default limit is 64.
  493. /// </remarks>
  494. public int SetRecursionLimit(int limit) {
  495. if (limit < 0) {
  496. throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
  497. }
  498. int oldLimit = recursionLimit;
  499. recursionLimit = limit;
  500. return oldLimit;
  501. }
  502. /// <summary>
  503. /// Set the maximum message size.
  504. /// </summary>
  505. /// <remarks>
  506. /// In order to prevent malicious messages from exhausting memory or
  507. /// causing integer overflows, CodedInputStream limits how large a message may be.
  508. /// The default limit is 64MB. You should set this limit as small
  509. /// as you can without harming your app's functionality. Note that
  510. /// size limits only apply when reading from an InputStream, not
  511. /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
  512. /// </remarks>
  513. public int SetSizeLimit(int limit) {
  514. if (limit < 0) {
  515. throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
  516. }
  517. int oldLimit = sizeLimit;
  518. sizeLimit = limit;
  519. return oldLimit;
  520. }
  521. #region Internal reading and buffer management
  522. /// <summary>
  523. /// Sets currentLimit to (current position) + byteLimit. This is called
  524. /// when descending into a length-delimited embedded message. The previous
  525. /// limit is returned.
  526. /// </summary>
  527. /// <returns>The old limit.</returns>
  528. public int PushLimit(int byteLimit) {
  529. if (byteLimit < 0) {
  530. throw InvalidProtocolBufferException.NegativeSize();
  531. }
  532. byteLimit += totalBytesRetired + bufferPos;
  533. int oldLimit = currentLimit;
  534. if (byteLimit > oldLimit) {
  535. throw InvalidProtocolBufferException.TruncatedMessage();
  536. }
  537. currentLimit = byteLimit;
  538. RecomputeBufferSizeAfterLimit();
  539. return oldLimit;
  540. }
  541. private void RecomputeBufferSizeAfterLimit() {
  542. bufferSize += bufferSizeAfterLimit;
  543. int bufferEnd = totalBytesRetired + bufferSize;
  544. if (bufferEnd > currentLimit) {
  545. // Limit is in current buffer.
  546. bufferSizeAfterLimit = bufferEnd - currentLimit;
  547. bufferSize -= bufferSizeAfterLimit;
  548. } else {
  549. bufferSizeAfterLimit = 0;
  550. }
  551. }
  552. /// <summary>
  553. /// Discards the current limit, returning the previous limit.
  554. /// </summary>
  555. public void PopLimit(int oldLimit) {
  556. currentLimit = oldLimit;
  557. RecomputeBufferSizeAfterLimit();
  558. }
  559. /// <summary>
  560. /// Called when buffer is empty to read more bytes from the
  561. /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
  562. /// either there will be at least one byte in the buffer when it returns
  563. /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
  564. /// RefillBuffer() returns false if no more bytes were available.
  565. /// </summary>
  566. /// <param name="mustSucceed"></param>
  567. /// <returns></returns>
  568. private bool RefillBuffer(bool mustSucceed) {
  569. if (bufferPos < bufferSize) {
  570. throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
  571. }
  572. if (totalBytesRetired + bufferSize == currentLimit) {
  573. // Oops, we hit a limit.
  574. if (mustSucceed) {
  575. throw InvalidProtocolBufferException.TruncatedMessage();
  576. } else {
  577. return false;
  578. }
  579. }
  580. totalBytesRetired += bufferSize;
  581. bufferPos = 0;
  582. bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
  583. if (bufferSize == 0) {
  584. if (mustSucceed) {
  585. throw InvalidProtocolBufferException.TruncatedMessage();
  586. } else {
  587. return false;
  588. }
  589. } else {
  590. RecomputeBufferSizeAfterLimit();
  591. int totalBytesRead =
  592. totalBytesRetired + bufferSize + bufferSizeAfterLimit;
  593. if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
  594. throw InvalidProtocolBufferException.SizeLimitExceeded();
  595. }
  596. return true;
  597. }
  598. }
  599. /// <summary>
  600. /// Read one byte from the input.
  601. /// </summary>
  602. /// <exception cref="InvalidProtocolBufferException">
  603. /// he end of the stream or the current limit was reached
  604. /// </exception>
  605. public byte ReadRawByte() {
  606. if (bufferPos == bufferSize) {
  607. RefillBuffer(true);
  608. }
  609. return buffer[bufferPos++];
  610. }
  611. /// <summary>
  612. /// Read a fixed size of bytes from the input.
  613. /// </summary>
  614. /// <exception cref="InvalidProtocolBufferException">
  615. /// the end of the stream or the current limit was reached
  616. /// </exception>
  617. public byte[] ReadRawBytes(int size) {
  618. if (size < 0) {
  619. throw InvalidProtocolBufferException.NegativeSize();
  620. }
  621. if (totalBytesRetired + bufferPos + size > currentLimit) {
  622. // Read to the end of the stream anyway.
  623. SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
  624. // Then fail.
  625. throw InvalidProtocolBufferException.TruncatedMessage();
  626. }
  627. if (size <= bufferSize - bufferPos) {
  628. // We have all the bytes we need already.
  629. byte[] bytes = new byte[size];
  630. Array.Copy(buffer, bufferPos, bytes, 0, size);
  631. bufferPos += size;
  632. return bytes;
  633. } else if (size < BufferSize) {
  634. // Reading more bytes than are in the buffer, but not an excessive number
  635. // of bytes. We can safely allocate the resulting array ahead of time.
  636. // First copy what we have.
  637. byte[] bytes = new byte[size];
  638. int pos = bufferSize - bufferPos;
  639. Array.Copy(buffer, bufferPos, bytes, 0, pos);
  640. bufferPos = bufferSize;
  641. // We want to use RefillBuffer() and then copy from the buffer into our
  642. // byte array rather than reading directly into our byte array because
  643. // the input may be unbuffered.
  644. RefillBuffer(true);
  645. while (size - pos > bufferSize) {
  646. Array.Copy(buffer, 0, bytes, pos, bufferSize);
  647. pos += bufferSize;
  648. bufferPos = bufferSize;
  649. RefillBuffer(true);
  650. }
  651. Array.Copy(buffer, 0, bytes, pos, size - pos);
  652. bufferPos = size - pos;
  653. return bytes;
  654. } else {
  655. // The size is very large. For security reasons, we can't allocate the
  656. // entire byte array yet. The size comes directly from the input, so a
  657. // maliciously-crafted message could provide a bogus very large size in
  658. // order to trick the app into allocating a lot of memory. We avoid this
  659. // by allocating and reading only a small chunk at a time, so that the
  660. // malicious message must actually *be* extremely large to cause
  661. // problems. Meanwhile, we limit the allowed size of a message elsewhere.
  662. // Remember the buffer markers since we'll have to copy the bytes out of
  663. // it later.
  664. int originalBufferPos = bufferPos;
  665. int originalBufferSize = bufferSize;
  666. // Mark the current buffer consumed.
  667. totalBytesRetired += bufferSize;
  668. bufferPos = 0;
  669. bufferSize = 0;
  670. // Read all the rest of the bytes we need.
  671. int sizeLeft = size - (originalBufferSize - originalBufferPos);
  672. List<byte[]> chunks = new List<byte[]>();
  673. while (sizeLeft > 0) {
  674. byte[] chunk = new byte[Math.Min(sizeLeft, BufferSize)];
  675. int pos = 0;
  676. while (pos < chunk.Length) {
  677. int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
  678. if (n <= 0) {
  679. throw InvalidProtocolBufferException.TruncatedMessage();
  680. }
  681. totalBytesRetired += n;
  682. pos += n;
  683. }
  684. sizeLeft -= chunk.Length;
  685. chunks.Add(chunk);
  686. }
  687. // OK, got everything. Now concatenate it all into one buffer.
  688. byte[] bytes = new byte[size];
  689. // Start by copying the leftover bytes from this.buffer.
  690. int newPos = originalBufferSize - originalBufferPos;
  691. Array.Copy(buffer, originalBufferPos, bytes, 0, newPos);
  692. // And now all the chunks.
  693. foreach (byte[] chunk in chunks) {
  694. Array.Copy(chunk, 0, bytes, newPos, chunk.Length);
  695. newPos += chunk.Length;
  696. }
  697. // Done.
  698. return bytes;
  699. }
  700. }
  701. /// <summary>
  702. /// Reads and discards a single field, given its tag value.
  703. /// </summary>
  704. /// <returns>false if the tag is an end-group tag, in which case
  705. /// nothing is skipped. Otherwise, returns true.</returns>
  706. public bool SkipField(uint tag) {
  707. switch (WireFormat.GetTagWireType(tag)) {
  708. case WireFormat.WireType.Varint:
  709. ReadInt32();
  710. return true;
  711. case WireFormat.WireType.Fixed64:
  712. ReadRawLittleEndian64();
  713. return true;
  714. case WireFormat.WireType.LengthDelimited:
  715. SkipRawBytes((int) ReadRawVarint32());
  716. return true;
  717. case WireFormat.WireType.StartGroup:
  718. SkipMessage();
  719. CheckLastTagWas(
  720. WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
  721. WireFormat.WireType.EndGroup));
  722. return true;
  723. case WireFormat.WireType.EndGroup:
  724. return false;
  725. case WireFormat.WireType.Fixed32:
  726. ReadRawLittleEndian32();
  727. return true;
  728. default:
  729. throw InvalidProtocolBufferException.InvalidWireType();
  730. }
  731. }
  732. /// <summary>
  733. /// Reads and discards an entire message. This will read either until EOF
  734. /// or until an endgroup tag, whichever comes first.
  735. /// </summary>
  736. public void SkipMessage() {
  737. while (true) {
  738. uint tag = ReadTag();
  739. if (tag == 0 || !SkipField(tag)) {
  740. return;
  741. }
  742. }
  743. }
  744. /// <summary>
  745. /// Reads and discards <paramref name="size"/> bytes.
  746. /// </summary>
  747. /// <exception cref="InvalidProtocolBufferException">the end of the stream
  748. /// or the current limit was reached</exception>
  749. public void SkipRawBytes(int size) {
  750. if (size < 0) {
  751. throw InvalidProtocolBufferException.NegativeSize();
  752. }
  753. if (totalBytesRetired + bufferPos + size > currentLimit) {
  754. // Read to the end of the stream anyway.
  755. SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
  756. // Then fail.
  757. throw InvalidProtocolBufferException.TruncatedMessage();
  758. }
  759. if (size < bufferSize - bufferPos) {
  760. // We have all the bytes we need already.
  761. bufferPos += size;
  762. } else {
  763. // Skipping more bytes than are in the buffer. First skip what we have.
  764. int pos = bufferSize - bufferPos;
  765. totalBytesRetired += pos;
  766. bufferPos = 0;
  767. bufferSize = 0;
  768. // Then skip directly from the InputStream for the rest.
  769. if (pos < size) {
  770. // TODO(jonskeet): Java implementation uses skip(). Not sure whether this is really equivalent...
  771. if (input == null) {
  772. throw InvalidProtocolBufferException.TruncatedMessage();
  773. }
  774. input.Seek(size - pos, SeekOrigin.Current);
  775. if (input.Position > input.Length) {
  776. throw InvalidProtocolBufferException.TruncatedMessage();
  777. }
  778. totalBytesRetired += size - pos;
  779. }
  780. }
  781. }
  782. #endregion
  783. }
  784. }