CodedInputStream.cs 25 KB

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