CodedInputStream.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  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 Uncategorised (TODO: Fix this!)
  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. return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
  131. }
  132. /// <summary>
  133. /// Read a float field from the stream.
  134. /// </summary>
  135. public float ReadFloat() {
  136. //return Float.intBitsToFloat(readRawLittleEndian32());
  137. // FIXME implement!
  138. throw new NotImplementedException();
  139. }
  140. /// <summary>
  141. /// Read a uint64 field from the stream.
  142. /// </summary>
  143. public ulong ReadUInt64() {
  144. return ReadRawVarint64();
  145. }
  146. /// <summary>
  147. /// Read an int64 field from the stream.
  148. /// </summary>
  149. public long ReadInt64() {
  150. return (long) ReadRawVarint64();
  151. }
  152. /// <summary>
  153. /// Read an int32 field from the stream.
  154. /// </summary>
  155. public int ReadInt32() {
  156. return (int) ReadRawVarint32();
  157. }
  158. /// <summary>
  159. /// Read a fixed64 field from the stream.
  160. /// </summary>
  161. public ulong ReadFixed64() {
  162. return ReadRawLittleEndian64();
  163. }
  164. /// <summary>
  165. /// Read a fixed32 field from the stream.
  166. /// </summary>
  167. public uint ReadFixed32() {
  168. return ReadRawLittleEndian32();
  169. }
  170. /// <summary>
  171. /// Read a bool field from the stream.
  172. /// </summary>
  173. public bool ReadBool() {
  174. return ReadRawVarint32() != 0;
  175. }
  176. /// <summary>
  177. /// Reads a string field from the stream.
  178. /// </summary>
  179. public String ReadString() {
  180. int size = (int) ReadRawVarint32();
  181. if (size < bufferSize - bufferPos && size > 0) {
  182. // Fast path: We already have the bytes in a contiguous buffer, so
  183. // just copy directly from it.
  184. String result = Encoding.UTF8.GetString(buffer, bufferPos, size);
  185. bufferPos += size;
  186. return result;
  187. } else {
  188. // Slow path: Build a byte array first then copy it.
  189. return Encoding.UTF8.GetString(ReadRawBytes(size));
  190. }
  191. }
  192. /// <summary>
  193. /// Reads a group field value from the stream.
  194. /// </summary>
  195. public void ReadGroup(int fieldNumber, IBuilder builder,
  196. ExtensionRegistry extensionRegistry) {
  197. if (recursionDepth >= recursionLimit) {
  198. throw InvalidProtocolBufferException.RecursionLimitExceeded();
  199. }
  200. ++recursionDepth;
  201. builder.MergeFrom(this, extensionRegistry);
  202. CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
  203. --recursionDepth;
  204. }
  205. /// <summary>
  206. /// Reads a group field value from the stream and merges it into the given
  207. /// UnknownFieldSet.
  208. /// </summary>
  209. public void ReadUnknownGroup(int fieldNumber, UnknownFieldSet.Builder builder) {
  210. if (recursionDepth >= recursionLimit) {
  211. throw InvalidProtocolBufferException.RecursionLimitExceeded();
  212. }
  213. ++recursionDepth;
  214. builder.MergeFrom(this);
  215. CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup));
  216. --recursionDepth;
  217. }
  218. /// <summary>
  219. /// Reads an embedded message field value from the stream.
  220. /// </summary>
  221. public void ReadMessage(IBuilder builder, ExtensionRegistry extensionRegistry) {
  222. int length = (int) ReadRawVarint32();
  223. if (recursionDepth >= recursionLimit) {
  224. throw InvalidProtocolBufferException.RecursionLimitExceeded();
  225. }
  226. int oldLimit = PushLimit(length);
  227. ++recursionDepth;
  228. builder.MergeFrom(this, extensionRegistry);
  229. CheckLastTagWas(0);
  230. --recursionDepth;
  231. PopLimit(oldLimit);
  232. }
  233. /// <summary>
  234. /// Reads a bytes field value from the stream.
  235. /// </summary>
  236. public ByteString ReadBytes() {
  237. int size = (int) ReadRawVarint32();
  238. if (size < bufferSize - bufferPos && size > 0) {
  239. // Fast path: We already have the bytes in a contiguous buffer, so
  240. // just copy directly from it.
  241. ByteString result = ByteString.CopyFrom(buffer, bufferPos, size);
  242. bufferPos += size;
  243. return result;
  244. } else {
  245. // Slow path: Build a byte array first then copy it.
  246. return ByteString.CopyFrom(ReadRawBytes(size));
  247. }
  248. }
  249. /// <summary>
  250. /// Reads a uint32 field value from the stream.
  251. /// </summary>
  252. public uint ReadUInt32() {
  253. return ReadRawVarint32();
  254. }
  255. /// <summary>
  256. /// Reads an enum field value from the stream. The caller is responsible
  257. /// for converting the numeric value to an actual enum.
  258. /// </summary>
  259. public int ReadEnum() {
  260. return (int) ReadRawVarint32();
  261. }
  262. /// <summary>
  263. /// Reads an sfixed32 field value from the stream.
  264. /// </summary>
  265. public int ReadSFixed32() {
  266. return (int) ReadRawLittleEndian32();
  267. }
  268. /// <summary>
  269. /// Reads an sfixed64 field value from the stream.
  270. /// </summary>
  271. public long ReadSFixed64() {
  272. return (long) ReadRawLittleEndian64();
  273. }
  274. /// <summary>
  275. /// Reads an sint32 field value from the stream.
  276. /// </summary>
  277. public int ReadSInt32() {
  278. return DecodeZigZag32(ReadRawVarint32());
  279. }
  280. /// <summary>
  281. /// Reads an sint64 field value from the stream.
  282. /// </summary>
  283. public long ReadSInt64() {
  284. return DecodeZigZag64(ReadRawVarint64());
  285. }
  286. /// <summary>
  287. /// Reads a field of any primitive type. Enums, groups and embedded
  288. /// messages are not handled by this method.
  289. /// </summary>
  290. public object ReadPrimitiveField(FieldType fieldType) {
  291. switch (fieldType) {
  292. case FieldType.Double: return ReadDouble();
  293. case FieldType.Float: return ReadFloat();
  294. case FieldType.Int64: return ReadInt64();
  295. case FieldType.UInt64: return ReadUInt64();
  296. case FieldType.Int32: return ReadInt32();
  297. case FieldType.Fixed64: return ReadFixed64();
  298. case FieldType.Fixed32: return ReadFixed32();
  299. case FieldType.Bool: return ReadBool();
  300. case FieldType.String: return ReadString();
  301. case FieldType.Bytes: return ReadBytes();
  302. case FieldType.UInt32: return ReadUInt32();
  303. case FieldType.SFixed32: return ReadSFixed32();
  304. case FieldType.SFixed64: return ReadSFixed64();
  305. case FieldType.SInt32: return ReadSInt32();
  306. case FieldType.SInt64: return ReadSInt64();
  307. case FieldType.Group:
  308. throw new ArgumentException("ReadPrimitiveField() cannot handle nested groups.");
  309. case FieldType.Message:
  310. throw new ArgumentException("ReadPrimitiveField() cannot handle embedded messages.");
  311. // We don't handle enums because we don't know what to do if the
  312. // value is not recognized.
  313. case FieldType.Enum:
  314. throw new ArgumentException("ReadPrimitiveField() cannot handle enums.");
  315. default:
  316. throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
  317. }
  318. }
  319. #endregion
  320. #region Underlying reading primitives
  321. /// <summary>
  322. /// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
  323. /// </summary>
  324. /// <returns></returns>
  325. public uint ReadRawVarint32() {
  326. int tmp = ReadRawByte();
  327. if (tmp < 128) {
  328. return (uint) tmp;
  329. }
  330. int result = tmp & 0x7f;
  331. if ((tmp = ReadRawByte()) < 128) {
  332. result |= tmp << 7;
  333. } else {
  334. result |= (tmp & 0x7f) << 7;
  335. if ((tmp = ReadRawByte()) < 128) {
  336. result |= tmp << 14;
  337. } else {
  338. result |= (tmp & 0x7f) << 14;
  339. if ((tmp = ReadRawByte()) < 128) {
  340. result |= tmp << 21;
  341. } else {
  342. result |= (tmp & 0x7f) << 21;
  343. result |= (tmp = ReadRawByte()) << 28;
  344. if (tmp >= 128) {
  345. // Discard upper 32 bits.
  346. for (int i = 0; i < 5; i++) {
  347. if (ReadRawByte() < 128) return (uint) result;
  348. }
  349. throw InvalidProtocolBufferException.MalformedVarint();
  350. }
  351. }
  352. }
  353. }
  354. return (uint) result;
  355. }
  356. /// <summary>
  357. /// Read a raw varint from the stream.
  358. /// </summary>
  359. public ulong ReadRawVarint64() {
  360. int shift = 0;
  361. ulong result = 0;
  362. while (shift < 64) {
  363. byte b = ReadRawByte();
  364. result |= (ulong)(b & 0x7F) << shift;
  365. if ((b & 0x80) == 0) {
  366. return result;
  367. }
  368. shift += 7;
  369. }
  370. throw InvalidProtocolBufferException.MalformedVarint();
  371. }
  372. /// <summary>
  373. /// Read a 32-bit little-endian integer from the stream.
  374. /// </summary>
  375. public uint ReadRawLittleEndian32() {
  376. uint b1 = ReadRawByte();
  377. uint b2 = ReadRawByte();
  378. uint b3 = ReadRawByte();
  379. uint b4 = ReadRawByte();
  380. return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
  381. }
  382. /// <summary>
  383. /// Read a 64-bit little-endian integer from the stream.
  384. /// </summary>
  385. public ulong ReadRawLittleEndian64() {
  386. ulong b1 = ReadRawByte();
  387. ulong b2 = ReadRawByte();
  388. ulong b3 = ReadRawByte();
  389. ulong b4 = ReadRawByte();
  390. ulong b5 = ReadRawByte();
  391. ulong b6 = ReadRawByte();
  392. ulong b7 = ReadRawByte();
  393. ulong b8 = ReadRawByte();
  394. return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
  395. | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
  396. }
  397. #endregion
  398. /// <summary>
  399. /// Decode a 32-bit value with ZigZag encoding.
  400. /// </summary>
  401. /// <remarks>
  402. /// ZigZag encodes signed integers into values that can be efficiently
  403. /// encoded with varint. (Otherwise, negative values must be
  404. /// sign-extended to 64 bits to be varint encoded, thus always taking
  405. /// 10 bytes on the wire.)
  406. /// </remarks>
  407. public static int DecodeZigZag32(uint n) {
  408. return (int)(n >> 1) ^ -(int)(n & 1);
  409. }
  410. /// <summary>
  411. /// Decode a 32-bit value with ZigZag encoding.
  412. /// </summary>
  413. /// <remarks>
  414. /// ZigZag encodes signed integers into values that can be efficiently
  415. /// encoded with varint. (Otherwise, negative values must be
  416. /// sign-extended to 64 bits to be varint encoded, thus always taking
  417. /// 10 bytes on the wire.)
  418. /// </remarks>
  419. public static long DecodeZigZag64(ulong n) {
  420. return (long)(n >> 1) ^ -(long)(n & 1);
  421. }
  422. /// <summary>
  423. /// Set the maximum message recursion depth.
  424. /// </summary>
  425. /// <remarks>
  426. /// In order to prevent malicious
  427. /// messages from causing stack overflows, CodedInputStream limits
  428. /// how deeply messages may be nested. The default limit is 64.
  429. /// </remarks>
  430. public int SetRecursionLimit(int limit) {
  431. if (limit < 0) {
  432. throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit);
  433. }
  434. int oldLimit = recursionLimit;
  435. recursionLimit = limit;
  436. return oldLimit;
  437. }
  438. /// <summary>
  439. /// Set the maximum message size.
  440. /// </summary>
  441. /// <remarks>
  442. /// In order to prevent malicious messages from exhausting memory or
  443. /// causing integer overflows, CodedInputStream limits how large a message may be.
  444. /// The default limit is 64MB. You should set this limit as small
  445. /// as you can without harming your app's functionality. Note that
  446. /// size limits only apply when reading from an InputStream, not
  447. /// when constructed around a raw byte array (nor with ByteString.NewCodedInput).
  448. /// </remarks>
  449. public int SetSizeLimit(int limit) {
  450. if (limit < 0) {
  451. throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit);
  452. }
  453. int oldLimit = sizeLimit;
  454. sizeLimit = limit;
  455. return oldLimit;
  456. }
  457. #region Internal reading and buffer management
  458. /// <summary>
  459. /// Sets currentLimit to (current position) + byteLimit. This is called
  460. /// when descending into a length-delimited embedded message. The previous
  461. /// limit is returned.
  462. /// </summary>
  463. /// <returns>The old limit.</returns>
  464. public int PushLimit(int byteLimit) {
  465. if (byteLimit < 0) {
  466. throw InvalidProtocolBufferException.NegativeSize();
  467. }
  468. byteLimit += totalBytesRetired + bufferPos;
  469. int oldLimit = currentLimit;
  470. if (byteLimit > oldLimit) {
  471. throw InvalidProtocolBufferException.TruncatedMessage();
  472. }
  473. currentLimit = byteLimit;
  474. RecomputeBufferSizeAfterLimit();
  475. return oldLimit;
  476. }
  477. private void RecomputeBufferSizeAfterLimit() {
  478. bufferSize += bufferSizeAfterLimit;
  479. int bufferEnd = totalBytesRetired + bufferSize;
  480. if (bufferEnd > currentLimit) {
  481. // Limit is in current buffer.
  482. bufferSizeAfterLimit = bufferEnd - currentLimit;
  483. bufferSize -= bufferSizeAfterLimit;
  484. } else {
  485. bufferSizeAfterLimit = 0;
  486. }
  487. }
  488. /// <summary>
  489. /// Discards the current limit, returning the previous limit.
  490. /// </summary>
  491. public void PopLimit(int oldLimit) {
  492. currentLimit = oldLimit;
  493. RecomputeBufferSizeAfterLimit();
  494. }
  495. /// <summary>
  496. /// Called when buffer is empty to read more bytes from the
  497. /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
  498. /// either there will be at least one byte in the buffer when it returns
  499. /// or it will throw an exception. If <paramref name="mustSucceed"/> is false,
  500. /// RefillBuffer() returns false if no more bytes were available.
  501. /// </summary>
  502. /// <param name="mustSucceed"></param>
  503. /// <returns></returns>
  504. private bool RefillBuffer(bool mustSucceed) {
  505. if (bufferPos < bufferSize) {
  506. throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
  507. }
  508. if (totalBytesRetired + bufferSize == currentLimit) {
  509. // Oops, we hit a limit.
  510. if (mustSucceed) {
  511. throw InvalidProtocolBufferException.TruncatedMessage();
  512. } else {
  513. return false;
  514. }
  515. }
  516. totalBytesRetired += bufferSize;
  517. bufferPos = 0;
  518. bufferSize = (input == null) ? -1 : input.Read(buffer, 0, buffer.Length);
  519. if (bufferSize == -1) {
  520. bufferSize = 0;
  521. if (mustSucceed) {
  522. throw InvalidProtocolBufferException.TruncatedMessage();
  523. } else {
  524. return false;
  525. }
  526. } else {
  527. RecomputeBufferSizeAfterLimit();
  528. int totalBytesRead =
  529. totalBytesRetired + bufferSize + bufferSizeAfterLimit;
  530. if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
  531. throw InvalidProtocolBufferException.SizeLimitExceeded();
  532. }
  533. return true;
  534. }
  535. }
  536. /// <summary>
  537. /// Read one byte from the input.
  538. /// </summary>
  539. /// <exception cref="InvalidProtocolBufferException">
  540. /// he end of the stream or the current limit was reached
  541. /// </exception>
  542. public byte ReadRawByte() {
  543. if (bufferPos == bufferSize) {
  544. RefillBuffer(true);
  545. }
  546. return buffer[bufferPos++];
  547. }
  548. /// <summary>
  549. /// Read a fixed size of bytes from the input.
  550. /// </summary>
  551. /// <exception cref="InvalidProtocolBufferException">
  552. /// the end of the stream or the current limit was reached
  553. /// </exception>
  554. public byte[] ReadRawBytes(int size) {
  555. if (size < 0) {
  556. throw InvalidProtocolBufferException.NegativeSize();
  557. }
  558. if (totalBytesRetired + bufferPos + size > currentLimit) {
  559. // Read to the end of the stream anyway.
  560. SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
  561. // Then fail.
  562. throw InvalidProtocolBufferException.TruncatedMessage();
  563. }
  564. if (size <= bufferSize - bufferPos) {
  565. // We have all the bytes we need already.
  566. byte[] bytes = new byte[size];
  567. Array.Copy(buffer, bufferPos, bytes, 0, size);
  568. bufferPos += size;
  569. return bytes;
  570. } else if (size < BufferSize) {
  571. // Reading more bytes than are in the buffer, but not an excessive number
  572. // of bytes. We can safely allocate the resulting array ahead of time.
  573. // First copy what we have.
  574. byte[] bytes = new byte[size];
  575. int pos = bufferSize - bufferPos;
  576. Array.Copy(buffer, bufferPos, bytes, 0, pos);
  577. bufferPos = bufferSize;
  578. // We want to use RefillBuffer() and then copy from the buffer into our
  579. // byte array rather than reading directly into our byte array because
  580. // the input may be unbuffered.
  581. RefillBuffer(true);
  582. while (size - pos > bufferSize) {
  583. Array.Copy(buffer, 0, bytes, pos, bufferSize);
  584. pos += bufferSize;
  585. bufferPos = bufferSize;
  586. RefillBuffer(true);
  587. }
  588. Array.Copy(buffer, 0, bytes, pos, size - pos);
  589. bufferPos = size - pos;
  590. return bytes;
  591. } else {
  592. // The size is very large. For security reasons, we can't allocate the
  593. // entire byte array yet. The size comes directly from the input, so a
  594. // maliciously-crafted message could provide a bogus very large size in
  595. // order to trick the app into allocating a lot of memory. We avoid this
  596. // by allocating and reading only a small chunk at a time, so that the
  597. // malicious message must actually *be* extremely large to cause
  598. // problems. Meanwhile, we limit the allowed size of a message elsewhere.
  599. // Remember the buffer markers since we'll have to copy the bytes out of
  600. // it later.
  601. int originalBufferPos = bufferPos;
  602. int originalBufferSize = bufferSize;
  603. // Mark the current buffer consumed.
  604. totalBytesRetired += bufferSize;
  605. bufferPos = 0;
  606. bufferSize = 0;
  607. // Read all the rest of the bytes we need.
  608. int sizeLeft = size - (originalBufferSize - originalBufferPos);
  609. List<byte[]> chunks = new List<byte[]>();
  610. while (sizeLeft > 0) {
  611. byte[] chunk = new byte[Math.Min(sizeLeft, BufferSize)];
  612. int pos = 0;
  613. while (pos < chunk.Length) {
  614. int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
  615. if (n <= 0) {
  616. throw InvalidProtocolBufferException.TruncatedMessage();
  617. }
  618. totalBytesRetired += n;
  619. pos += n;
  620. }
  621. sizeLeft -= chunk.Length;
  622. chunks.Add(chunk);
  623. }
  624. // OK, got everything. Now concatenate it all into one buffer.
  625. byte[] bytes = new byte[size];
  626. // Start by copying the leftover bytes from this.buffer.
  627. int newPos = originalBufferSize - originalBufferPos;
  628. Array.Copy(buffer, originalBufferPos, bytes, 0, newPos);
  629. // And now all the chunks.
  630. foreach (byte[] chunk in chunks) {
  631. Array.Copy(chunk, 0, bytes, newPos, chunk.Length);
  632. newPos += chunk.Length;
  633. }
  634. // Done.
  635. return bytes;
  636. }
  637. }
  638. /// <summary>
  639. /// Reads and discards a single field, given its tag value.
  640. /// </summary>
  641. /// <returns>false if the tag is an end-group tag, in which case
  642. /// nothing is skipped. Otherwise, returns true.</returns>
  643. public bool SkipField(uint tag) {
  644. switch (WireFormat.GetTagWireType(tag)) {
  645. case WireFormat.WireType.Varint:
  646. ReadInt32();
  647. return true;
  648. case WireFormat.WireType.Fixed64:
  649. ReadRawLittleEndian64();
  650. return true;
  651. case WireFormat.WireType.LengthDelimited:
  652. SkipRawBytes((int) ReadRawVarint32());
  653. return true;
  654. case WireFormat.WireType.StartGroup:
  655. SkipMessage();
  656. CheckLastTagWas(
  657. WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag),
  658. WireFormat.WireType.EndGroup));
  659. return true;
  660. case WireFormat.WireType.EndGroup:
  661. return false;
  662. case WireFormat.WireType.Fixed32:
  663. ReadRawLittleEndian32();
  664. return true;
  665. default:
  666. throw InvalidProtocolBufferException.InvalidWireType();
  667. }
  668. }
  669. /// <summary>
  670. /// Reads and discards an entire message. This will read either until EOF
  671. /// or until an endgroup tag, whichever comes first.
  672. /// </summary>
  673. public void SkipMessage() {
  674. while (true) {
  675. uint tag = ReadTag();
  676. if (tag == 0 || !SkipField(tag)) {
  677. return;
  678. }
  679. }
  680. }
  681. /// <summary>
  682. /// Reads and discards <paramref name="size"/> bytes.
  683. /// </summary>
  684. /// <exception cref="InvalidProtocolBufferException">the end of the stream
  685. /// or the current limit was reached</exception>
  686. public void SkipRawBytes(int size) {
  687. if (size < 0) {
  688. throw InvalidProtocolBufferException.NegativeSize();
  689. }
  690. if (totalBytesRetired + bufferPos + size > currentLimit) {
  691. // Read to the end of the stream anyway.
  692. SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
  693. // Then fail.
  694. throw InvalidProtocolBufferException.TruncatedMessage();
  695. }
  696. if (size < bufferSize - bufferPos) {
  697. // We have all the bytes we need already.
  698. bufferPos += size;
  699. } else {
  700. // Skipping more bytes than are in the buffer. First skip what we have.
  701. int pos = bufferSize - bufferPos;
  702. totalBytesRetired += pos;
  703. bufferPos = 0;
  704. bufferSize = 0;
  705. // Then skip directly from the InputStream for the rest.
  706. if (pos < size) {
  707. // TODO(jonskeet): Java implementation uses skip(). Not sure whether this is really equivalent...
  708. if (input == null) {
  709. throw InvalidProtocolBufferException.TruncatedMessage();
  710. }
  711. input.Seek(size - pos, SeekOrigin.Current);
  712. if (input.Position > input.Length) {
  713. throw InvalidProtocolBufferException.TruncatedMessage();
  714. }
  715. totalBytesRetired += size - pos;
  716. }
  717. }
  718. }
  719. #endregion
  720. }
  721. }