CodedInputStreamTest.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using NUnit.Framework;
  5. using System.IO;
  6. namespace Google.ProtocolBuffers {
  7. [TestFixture]
  8. public class CodedInputStreamTest {
  9. /// <summary>
  10. /// Helper to construct a byte array from a bunch of bytes. The inputs are
  11. /// actually ints so that I can use hex notation and not get stupid errors
  12. /// about precision.
  13. /// </summary>
  14. private static byte[] Bytes(params int[] bytesAsInts) {
  15. byte[] bytes = new byte[bytesAsInts.Length];
  16. for (int i = 0; i < bytesAsInts.Length; i++) {
  17. bytes[i] = (byte)bytesAsInts[i];
  18. }
  19. return bytes;
  20. }
  21. /// <summary>
  22. /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
  23. /// </summary>
  24. private static void AssertReadVarint(byte[] data, ulong value) {
  25. CodedInputStream input = CodedInputStream.CreateInstance(data);
  26. Assert.AreEqual((uint)value, input.ReadRawVarint32());
  27. input = CodedInputStream.CreateInstance(data);
  28. Assert.AreEqual(value, input.ReadRawVarint64());
  29. // Try different block sizes.
  30. for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2) {
  31. input = CodedInputStream.CreateInstance(new SmallBlockInputStream(data, bufferSize));
  32. Assert.AreEqual((uint)value, input.ReadRawVarint32());
  33. input = CodedInputStream.CreateInstance(new SmallBlockInputStream(data, bufferSize));
  34. Assert.AreEqual(value, input.ReadRawVarint64());
  35. }
  36. }
  37. /// <summary>
  38. /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
  39. /// expects them to fail with an InvalidProtocolBufferException whose
  40. /// description matches the given one.
  41. /// </summary>
  42. private void AssertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data) {
  43. CodedInputStream input = CodedInputStream.CreateInstance(data);
  44. try {
  45. input.ReadRawVarint32();
  46. Assert.Fail("Should have thrown an exception.");
  47. } catch (InvalidProtocolBufferException e) {
  48. Assert.AreEqual(expected.Message, e.Message);
  49. }
  50. input = CodedInputStream.CreateInstance(data);
  51. try {
  52. input.ReadRawVarint64();
  53. Assert.Fail("Should have thrown an exception.");
  54. } catch (InvalidProtocolBufferException e) {
  55. Assert.AreEqual(expected.Message, e.Message);
  56. }
  57. }
  58. [Test]
  59. public void ReadVarint() {
  60. AssertReadVarint(Bytes(0x00), 0);
  61. AssertReadVarint(Bytes(0x01), 1);
  62. AssertReadVarint(Bytes(0x7f), 127);
  63. // 14882
  64. AssertReadVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
  65. // 2961488830
  66. AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
  67. (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
  68. (0x0bL << 28));
  69. // 64-bit
  70. // 7256456126
  71. AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
  72. (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
  73. (0x1bL << 28));
  74. // 41256202580718336
  75. AssertReadVarint(Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
  76. (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
  77. (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
  78. // 11964378330978735131
  79. AssertReadVarint(Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
  80. (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
  81. (0x3bUL << 28) | (0x56UL << 35) | (0x00UL << 42) |
  82. (0x05UL << 49) | (0x26UL << 56) | (0x01UL << 63));
  83. // Failures
  84. AssertReadVarintFailure(
  85. InvalidProtocolBufferException.MalformedVarint(),
  86. Bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
  87. 0x00));
  88. AssertReadVarintFailure(
  89. InvalidProtocolBufferException.TruncatedMessage(),
  90. Bytes(0x80));
  91. }
  92. /// <summary>
  93. /// Parses the given bytes using ReadRawLittleEndian32() and checks
  94. /// that the result matches the given value.
  95. /// </summary>
  96. private static void AssertReadLittleEndian32(byte[] data, int value) {
  97. CodedInputStream input = CodedInputStream.CreateInstance(data);
  98. Assert.AreEqual(value, input.ReadRawLittleEndian32());
  99. // Try different block sizes.
  100. for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
  101. input = CodedInputStream.CreateInstance(
  102. new SmallBlockInputStream(data, blockSize));
  103. Assert.AreEqual(value, input.ReadRawLittleEndian32());
  104. }
  105. }
  106. /// <summary>
  107. /// Parses the given bytes using ReadRawLittleEndian64() and checks
  108. /// that the result matches the given value.
  109. /// </summary>
  110. private static void AssertReadLittleEndian64(byte[] data, long value) {
  111. CodedInputStream input = CodedInputStream.CreateInstance(data);
  112. Assert.AreEqual(value, input.ReadRawLittleEndian64());
  113. // Try different block sizes.
  114. for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
  115. input = CodedInputStream.CreateInstance(
  116. new SmallBlockInputStream(data, blockSize));
  117. Assert.AreEqual(value, input.ReadRawLittleEndian64());
  118. }
  119. }
  120. [Test]
  121. public void ReadLittleEndian() {
  122. AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
  123. AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), unchecked((int)0x9abcdef0));
  124. AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
  125. 0x123456789abcdef0L);
  126. AssertReadLittleEndian64(
  127. Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), unchecked((long)0x9abcdef012345678L));
  128. }
  129. [Test]
  130. public void DecodeZigZag32() {
  131. Assert.AreEqual(0, CodedInputStream.DecodeZigZag32(0));
  132. Assert.AreEqual(-1, CodedInputStream.DecodeZigZag32(1));
  133. Assert.AreEqual(1, CodedInputStream.DecodeZigZag32(2));
  134. Assert.AreEqual(-2, CodedInputStream.DecodeZigZag32(3));
  135. Assert.AreEqual(0x3FFFFFFF, CodedInputStream.DecodeZigZag32(0x7FFFFFFE));
  136. Assert.AreEqual(unchecked((int)0xC0000000), CodedInputStream.DecodeZigZag32(0x7FFFFFFF));
  137. Assert.AreEqual(0x7FFFFFFF, CodedInputStream.DecodeZigZag32(0xFFFFFFFE));
  138. Assert.AreEqual(unchecked((int)0x80000000), CodedInputStream.DecodeZigZag32(0xFFFFFFFF));
  139. }
  140. [Test]
  141. public void DecodeZigZag64() {
  142. Assert.AreEqual(0, CodedInputStream.DecodeZigZag64(0));
  143. Assert.AreEqual(-1, CodedInputStream.DecodeZigZag64(1));
  144. Assert.AreEqual(1, CodedInputStream.DecodeZigZag64(2));
  145. Assert.AreEqual(-2, CodedInputStream.DecodeZigZag64(3));
  146. Assert.AreEqual(0x000000003FFFFFFFL, CodedInputStream.DecodeZigZag64(0x000000007FFFFFFEL));
  147. Assert.AreEqual(unchecked((long)0xFFFFFFFFC0000000L), CodedInputStream.DecodeZigZag64(0x000000007FFFFFFFL));
  148. Assert.AreEqual(0x000000007FFFFFFFL, CodedInputStream.DecodeZigZag64(0x00000000FFFFFFFEL));
  149. Assert.AreEqual(unchecked((long)0xFFFFFFFF80000000L), CodedInputStream.DecodeZigZag64(0x00000000FFFFFFFFL));
  150. Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
  151. Assert.AreEqual(unchecked((long)0x8000000000000000L), CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
  152. }
  153. /* TODO(jonskeet): Reinstate this when protoc is ready
  154. public void testReadWholeMessage() throws Exception {
  155. TestAllTypes message = TestUtil.getAllSet();
  156. byte[] rawBytes = message.toByteArray();
  157. assertEquals(rawBytes.length, message.getSerializedSize());
  158. TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
  159. TestUtil.assertAllFieldsSet(message2);
  160. // Try different block sizes.
  161. for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
  162. message2 = TestAllTypes.parseFrom(
  163. new SmallBlockInputStream(rawBytes, blockSize));
  164. TestUtil.assertAllFieldsSet(message2);
  165. }
  166. }*/
  167. /* TODO(jonskeet): Reinstate this when protoc is ready
  168. public void testSkipWholeMessage() throws Exception {
  169. TestAllTypes message = TestUtil.getAllSet();
  170. byte[] rawBytes = message.toByteArray();
  171. // Create two parallel inputs. Parse one as unknown fields while using
  172. // skipField() to skip each field on the other. Expect the same tags.
  173. CodedInputStream input1 = CodedInputStream.newInstance(rawBytes);
  174. CodedInputStream input2 = CodedInputStream.newInstance(rawBytes);
  175. UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder();
  176. while (true) {
  177. int tag = input1.readTag();
  178. assertEquals(tag, input2.readTag());
  179. if (tag == 0) {
  180. break;
  181. }
  182. unknownFields.mergeFieldFrom(tag, input1);
  183. input2.skipField(tag);
  184. }
  185. }*/
  186. /* TODO(jonskeet): Reinstate this when protoc is ready
  187. public void testReadHugeBlob() throws Exception {
  188. // Allocate and initialize a 1MB blob.
  189. byte[] blob = new byte[1 << 20];
  190. for (int i = 0; i < blob.length; i++) {
  191. blob[i] = (byte)i;
  192. }
  193. // Make a message containing it.
  194. TestAllTypes.Builder builder = TestAllTypes.newBuilder();
  195. TestUtil.setAllFields(builder);
  196. builder.setOptionalBytes(ByteString.copyFrom(blob));
  197. TestAllTypes message = builder.build();
  198. // Serialize and parse it. Make sure to parse from an InputStream, not
  199. // directly from a ByteString, so that CodedInputStream uses buffered
  200. // reading.
  201. TestAllTypes message2 =
  202. TestAllTypes.parseFrom(message.toByteString().newInput());
  203. assertEquals(message.getOptionalBytes(), message2.getOptionalBytes());
  204. // Make sure all the other fields were parsed correctly.
  205. TestAllTypes message3 = TestAllTypes.newBuilder(message2)
  206. .setOptionalBytes(TestUtil.getAllSet().getOptionalBytes())
  207. .build();
  208. TestUtil.assertAllFieldsSet(message3);
  209. }*/
  210. [Test]
  211. public void ReadMaliciouslyLargeBlob() {
  212. MemoryStream ms = new MemoryStream();
  213. CodedOutputStream output = CodedOutputStream.CreateInstance(ms);
  214. uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
  215. output.WriteRawVarint32(tag);
  216. output.WriteRawVarint32(0x7FFFFFFF);
  217. output.WriteRawBytes(new byte[32]); // Pad with a few random bytes.
  218. output.Flush();
  219. ms.Position = 0;
  220. CodedInputStream input = CodedInputStream.CreateInstance(ms);
  221. Assert.AreEqual(tag, input.ReadTag());
  222. try {
  223. input.ReadBytes();
  224. Assert.Fail("Should have thrown an exception!");
  225. } catch (InvalidProtocolBufferException) {
  226. // success.
  227. }
  228. }
  229. /* TODO(jonskeet): Reinstate this when protoc is ready
  230. private TestRecursiveMessage makeRecursiveMessage(int depth) {
  231. if (depth == 0) {
  232. return TestRecursiveMessage.newBuilder().setI(5).build();
  233. } else {
  234. return TestRecursiveMessage.newBuilder()
  235. .setA(makeRecursiveMessage(depth - 1)).build();
  236. }
  237. }
  238. private void assertMessageDepth(TestRecursiveMessage message, int depth) {
  239. if (depth == 0) {
  240. assertFalse(message.hasA());
  241. assertEquals(5, message.getI());
  242. } else {
  243. assertTrue(message.hasA());
  244. assertMessageDepth(message.getA(), depth - 1);
  245. }
  246. }
  247. public void testMaliciousRecursion() {
  248. ByteString data64 = makeRecursiveMessage(64).toByteString();
  249. ByteString data65 = makeRecursiveMessage(65).toByteString();
  250. assertMessageDepth(TestRecursiveMessage.parseFrom(data64), 64);
  251. try {
  252. TestRecursiveMessage.parseFrom(data65);
  253. fail("Should have thrown an exception!");
  254. } catch (InvalidProtocolBufferException e) {
  255. // success.
  256. }
  257. CodedInputStream input = data64.newCodedInput();
  258. input.setRecursionLimit(8);
  259. try {
  260. TestRecursiveMessage.parseFrom(input);
  261. fail("Should have thrown an exception!");
  262. } catch (InvalidProtocolBufferException e) {
  263. // success.
  264. }
  265. }
  266. */
  267. /* TODO(jonskeet): Reinstate this when protoc is ready
  268. public void testSizeLimit() throws Exception {
  269. CodedInputStream input = CodedInputStream.newInstance(
  270. TestUtil.getAllSet().toByteString().newInput());
  271. input.setSizeLimit(16);
  272. try {
  273. TestAllTypes.parseFrom(input);
  274. fail("Should have thrown an exception!");
  275. } catch (InvalidProtocolBufferException e) {
  276. // success.
  277. }
  278. }*/
  279. /// <summary>
  280. /// Tests that if we read an string that contains invalid UTF-8, no exception
  281. /// is thrown. Instead, the invalid bytes are replaced with the Unicode
  282. /// "replacement character" U+FFFD.
  283. /// </summary>
  284. [Test]
  285. public void ReadInvalidUtf8() {
  286. MemoryStream ms = new MemoryStream();
  287. CodedOutputStream output = CodedOutputStream.CreateInstance(ms);
  288. uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
  289. output.WriteRawVarint32(tag);
  290. output.WriteRawVarint32(1);
  291. output.WriteRawBytes(new byte[] { 0x80 });
  292. output.Flush();
  293. ms.Position = 0;
  294. CodedInputStream input = CodedInputStream.CreateInstance(ms);
  295. Assert.AreEqual(tag, input.ReadTag());
  296. string text = input.ReadString();
  297. Assert.AreEqual('\ufffd', text[0]);
  298. }
  299. /// <summary>
  300. /// A stream which limits the number of bytes it reads at a time.
  301. /// We use this to make sure that CodedInputStream doesn't screw up when
  302. /// reading in small blocks.
  303. /// </summary>
  304. private sealed class SmallBlockInputStream : MemoryStream {
  305. private readonly int blockSize;
  306. public SmallBlockInputStream(byte[] data, int blockSize)
  307. : base(data) {
  308. this.blockSize = blockSize;
  309. }
  310. public override int Read(byte[] buffer, int offset, int count) {
  311. return base.Read(buffer, offset, Math.Min(count, blockSize));
  312. }
  313. }
  314. }
  315. }