CodedInputStream.cs 26 KB

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