CodedInputStream.cs 28 KB

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