aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/protobuf/3.4.0/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/protobuf/3.4.0/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java')
-rw-r--r--third_party/protobuf/3.4.0/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java1094
1 files changed, 1094 insertions, 0 deletions
diff --git a/third_party/protobuf/3.4.0/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/third_party/protobuf/3.4.0/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
new file mode 100644
index 0000000000..da2c067ebb
--- /dev/null
+++ b/third_party/protobuf/3.4.0/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
@@ -0,0 +1,1094 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import protobuf_unittest.UnittestProto.BoolMessage;
+import protobuf_unittest.UnittestProto.Int32Message;
+import protobuf_unittest.UnittestProto.Int64Message;
+import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.TestRecursiveMessage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import junit.framework.TestCase;
+
+/**
+ * Unit test for {@link CodedInputStream}.
+ *
+ * @author kenton@google.com Kenton Varda
+ */
+public class CodedInputStreamTest extends TestCase {
+ private enum InputType {
+ ARRAY {
+ @Override
+ CodedInputStream newDecoder(byte[] data, int blockSize) {
+ return CodedInputStream.newInstance(data);
+ }
+ },
+ NIO_HEAP {
+ @Override
+ CodedInputStream newDecoder(byte[] data, int blockSize) {
+ return CodedInputStream.newInstance(ByteBuffer.wrap(data));
+ }
+ },
+ NIO_DIRECT {
+ @Override
+ CodedInputStream newDecoder(byte[] data, int blockSize) {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(data.length);
+ buffer.put(data);
+ buffer.flip();
+ return CodedInputStream.newInstance(buffer);
+ }
+ },
+ STREAM {
+ @Override
+ CodedInputStream newDecoder(byte[] data, int blockSize) {
+ return CodedInputStream.newInstance(new SmallBlockInputStream(data, blockSize));
+ }
+ };
+
+ CodedInputStream newDecoder(byte[] data) {
+ return newDecoder(data, data.length);
+ }
+
+ abstract CodedInputStream newDecoder(byte[] data, int blockSize);
+ }
+
+ /**
+ * Helper to construct a byte array from a bunch of bytes. The inputs are actually ints so that I
+ * can use hex notation and not get stupid errors about precision.
+ */
+ private byte[] bytes(int... bytesAsInts) {
+ byte[] bytes = new byte[bytesAsInts.length];
+ for (int i = 0; i < bytesAsInts.length; i++) {
+ bytes[i] = (byte) bytesAsInts[i];
+ }
+ return bytes;
+ }
+
+ /**
+ * An InputStream which limits the number of bytes it reads at a time. We use this to make sure
+ * that CodedInputStream doesn't screw up when reading in small blocks.
+ */
+ private static final class SmallBlockInputStream extends FilterInputStream {
+ private final int blockSize;
+
+ public SmallBlockInputStream(byte[] data, int blockSize) {
+ super(new ByteArrayInputStream(data));
+ this.blockSize = blockSize;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ return super.read(b, 0, Math.min(b.length, blockSize));
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return super.read(b, off, Math.min(len, blockSize));
+ }
+ }
+
+ private void assertDataConsumed(String msg, byte[] data, CodedInputStream input)
+ throws IOException {
+ assertEquals(msg, data.length, input.getTotalBytesRead());
+ assertTrue(msg, input.isAtEnd());
+ }
+
+ /**
+ * Parses the given bytes using readRawVarint32() and readRawVarint64() and checks that the result
+ * matches the given value.
+ */
+ private void assertReadVarint(byte[] data, long value) throws Exception {
+ for (InputType inputType : InputType.values()) {
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+ CodedInputStream input = inputType.newDecoder(data, blockSize);
+ assertEquals(inputType.name(), (int) value, input.readRawVarint32());
+ assertDataConsumed(inputType.name(), data, input);
+
+ input = inputType.newDecoder(data, blockSize);
+ assertEquals(inputType.name(), value, input.readRawVarint64());
+ assertDataConsumed(inputType.name(), data, input);
+
+ input = inputType.newDecoder(data, blockSize);
+ assertEquals(inputType.name(), value, input.readRawVarint64SlowPath());
+ assertDataConsumed(inputType.name(), data, input);
+
+ input = inputType.newDecoder(data, blockSize);
+ assertTrue(inputType.name(), input.skipField(WireFormat.WIRETYPE_VARINT));
+ assertDataConsumed(inputType.name(), data, input);
+ }
+ }
+
+ // Try reading direct from an InputStream. We want to verify that it
+ // doesn't read past the end of the input, so we copy to a new, bigger
+ // array first.
+ byte[] longerData = new byte[data.length + 1];
+ System.arraycopy(data, 0, longerData, 0, data.length);
+ InputStream rawInput = new ByteArrayInputStream(longerData);
+ assertEquals((int) value, CodedInputStream.readRawVarint32(rawInput));
+ assertEquals(1, rawInput.available());
+ }
+
+ /**
+ * Parses the given bytes using readRawVarint32() and readRawVarint64() and expects them to fail
+ * with an InvalidProtocolBufferException whose description matches the given one.
+ */
+ private void assertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)
+ throws Exception {
+ for (InputType inputType : InputType.values()) {
+ try {
+ CodedInputStream input = inputType.newDecoder(data);
+ input.readRawVarint32();
+ fail(inputType.name() + ": Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ assertEquals(inputType.name(), expected.getMessage(), e.getMessage());
+ }
+ try {
+ CodedInputStream input = inputType.newDecoder(data);
+ input.readRawVarint64();
+ fail(inputType.name() + ": Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ assertEquals(inputType.name(), expected.getMessage(), e.getMessage());
+ }
+ }
+
+ // Make sure we get the same error when reading direct from an InputStream.
+ try {
+ CodedInputStream.readRawVarint32(new ByteArrayInputStream(data));
+ fail("Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ assertEquals(expected.getMessage(), e.getMessage());
+ }
+ }
+
+ /** Tests readRawVarint32() and readRawVarint64(). */
+ public void testReadVarint() throws Exception {
+ assertReadVarint(bytes(0x00), 0);
+ assertReadVarint(bytes(0x01), 1);
+ assertReadVarint(bytes(0x7f), 127);
+ // 14882
+ assertReadVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
+ // 2961488830
+ assertReadVarint(
+ bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x0bL << 28));
+
+ // 64-bit
+ // 7256456126
+ assertReadVarint(
+ bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x1bL << 28));
+ // 41256202580718336
+ assertReadVarint(
+ bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
+ (0x00 << 0)
+ | (0x66 << 7)
+ | (0x6b << 14)
+ | (0x1c << 21)
+ | (0x43L << 28)
+ | (0x49L << 35)
+ | (0x24L << 42)
+ | (0x49L << 49));
+ // 11964378330978735131
+ assertReadVarint(
+ bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
+ (0x1b << 0)
+ | (0x28 << 7)
+ | (0x79 << 14)
+ | (0x42 << 21)
+ | (0x3bL << 28)
+ | (0x56L << 35)
+ | (0x00L << 42)
+ | (0x05L << 49)
+ | (0x26L << 56)
+ | (0x01L << 63));
+
+ // Failures
+ assertReadVarintFailure(
+ InvalidProtocolBufferException.malformedVarint(),
+ bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00));
+ assertReadVarintFailure(InvalidProtocolBufferException.truncatedMessage(), bytes(0x80));
+ }
+
+ /**
+ * Parses the given bytes using readRawLittleEndian32() and checks that the result matches the
+ * given value.
+ */
+ private void assertReadLittleEndian32(byte[] data, int value) throws Exception {
+ for (InputType inputType : InputType.values()) {
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+ CodedInputStream input = inputType.newDecoder(data, blockSize);
+ assertEquals(inputType.name(), value, input.readRawLittleEndian32());
+ assertTrue(inputType.name(), input.isAtEnd());
+ }
+ }
+ }
+
+ /**
+ * Parses the given bytes using readRawLittleEndian64() and checks that the result matches the
+ * given value.
+ */
+ private void assertReadLittleEndian64(byte[] data, long value) throws Exception {
+ for (InputType inputType : InputType.values()) {
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+ CodedInputStream input = inputType.newDecoder(data, blockSize);
+ assertEquals(inputType.name(), value, input.readRawLittleEndian64());
+ assertTrue(inputType.name(), input.isAtEnd());
+ }
+ }
+ }
+
+ /** Tests readRawLittleEndian32() and readRawLittleEndian64(). */
+ public void testReadLittleEndian() throws Exception {
+ assertReadLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
+ assertReadLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
+
+ assertReadLittleEndian64(
+ bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), 0x123456789abcdef0L);
+ assertReadLittleEndian64(
+ bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678L);
+ }
+
+ /** Test decodeZigZag32() and decodeZigZag64(). */
+ public void testDecodeZigZag() throws Exception {
+ assertEquals(0, CodedInputStream.decodeZigZag32(0));
+ assertEquals(-1, CodedInputStream.decodeZigZag32(1));
+ assertEquals(1, CodedInputStream.decodeZigZag32(2));
+ assertEquals(-2, CodedInputStream.decodeZigZag32(3));
+ assertEquals(0x3FFFFFFF, CodedInputStream.decodeZigZag32(0x7FFFFFFE));
+ assertEquals(0xC0000000, CodedInputStream.decodeZigZag32(0x7FFFFFFF));
+ assertEquals(0x7FFFFFFF, CodedInputStream.decodeZigZag32(0xFFFFFFFE));
+ assertEquals(0x80000000, CodedInputStream.decodeZigZag32(0xFFFFFFFF));
+
+ assertEquals(0, CodedInputStream.decodeZigZag64(0));
+ assertEquals(-1, CodedInputStream.decodeZigZag64(1));
+ assertEquals(1, CodedInputStream.decodeZigZag64(2));
+ assertEquals(-2, CodedInputStream.decodeZigZag64(3));
+ assertEquals(0x000000003FFFFFFFL, CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL));
+ assertEquals(0xFFFFFFFFC0000000L, CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL));
+ assertEquals(0x000000007FFFFFFFL, CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL));
+ assertEquals(0xFFFFFFFF80000000L, CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL));
+ assertEquals(0x7FFFFFFFFFFFFFFFL, CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL));
+ assertEquals(0x8000000000000000L, CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL));
+ }
+
+ /** Tests reading and parsing a whole message with every field type. */
+ public void testReadWholeMessage() throws Exception {
+ TestAllTypes message = TestUtil.getAllSet();
+
+ byte[] rawBytes = message.toByteArray();
+ assertEquals(rawBytes.length, message.getSerializedSize());
+
+ for (InputType inputType : InputType.values()) {
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
+ TestAllTypes message2 = TestAllTypes.parseFrom(inputType.newDecoder(rawBytes, blockSize));
+ TestUtil.assertAllFieldsSet(message2);
+ }
+ }
+ }
+
+ /** Tests skipField(). */
+ public void testSkipWholeMessage() throws Exception {
+ TestAllTypes message = TestUtil.getAllSet();
+ byte[] rawBytes = message.toByteArray();
+
+ InputType[] inputTypes = InputType.values();
+ CodedInputStream[] inputs = new CodedInputStream[inputTypes.length];
+ for (int i = 0; i < inputs.length; ++i) {
+ inputs[i] = inputTypes[i].newDecoder(rawBytes);
+ }
+ UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder();
+
+ while (true) {
+ CodedInputStream input1 = inputs[0];
+ int tag = input1.readTag();
+ // Ensure that the rest match.
+ for (int i = 1; i < inputs.length; ++i) {
+ assertEquals(inputTypes[i].name(), tag, inputs[i].readTag());
+ }
+ if (tag == 0) {
+ break;
+ }
+ unknownFields.mergeFieldFrom(tag, input1);
+ // Skip the field for the rest of the inputs.
+ for (int i = 1; i < inputs.length; ++i) {
+ inputs[i].skipField(tag);
+ }
+ }
+ }
+
+
+ /**
+ * Test that a bug in skipRawBytes() has been fixed: if the skip skips exactly up to a limit, this
+ * should not break things.
+ */
+ public void testSkipRawBytesBug() throws Exception {
+ byte[] rawBytes = new byte[] {1, 2};
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawBytes);
+ int limit = input.pushLimit(1);
+ input.skipRawBytes(1);
+ input.popLimit(limit);
+ assertEquals(inputType.name(), 2, input.readRawByte());
+ }
+ }
+
+ /**
+ * Test that a bug in skipRawBytes() has been fixed: if the skip skips past the end of a buffer
+ * with a limit that has been set past the end of that buffer, this should not break things.
+ */
+ public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception {
+ byte[] rawBytes = new byte[] {1, 2, 3, 4, 5};
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawBytes);
+ int limit = input.pushLimit(4);
+ // In order to expose the bug we need to read at least one byte to prime the
+ // buffer inside the CodedInputStream.
+ assertEquals(inputType.name(), 1, input.readRawByte());
+ // Skip to the end of the limit.
+ input.skipRawBytes(3);
+ assertTrue(inputType.name(), input.isAtEnd());
+ input.popLimit(limit);
+ assertEquals(inputType.name(), 5, input.readRawByte());
+ }
+ }
+
+ public void testReadHugeBlob() throws Exception {
+ // Allocate and initialize a 1MB blob.
+ byte[] blob = new byte[1 << 20];
+ for (int i = 0; i < blob.length; i++) {
+ blob[i] = (byte) i;
+ }
+
+ // Make a message containing it.
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ TestUtil.setAllFields(builder);
+ builder.setOptionalBytes(ByteString.copyFrom(blob));
+ TestAllTypes message = builder.build();
+
+ byte[] data = message.toByteArray();
+ for (InputType inputType : InputType.values()) {
+ // Serialize and parse it. Make sure to parse from an InputStream, not
+ // directly from a ByteString, so that CodedInputStream uses buffered
+ // reading.
+ TestAllTypes message2 = TestAllTypes.parseFrom(inputType.newDecoder(data));
+
+ assertEquals(inputType.name(), message.getOptionalBytes(), message2.getOptionalBytes());
+
+ // Make sure all the other fields were parsed correctly.
+ TestAllTypes message3 =
+ TestAllTypes.newBuilder(message2)
+ .setOptionalBytes(TestUtil.getAllSet().getOptionalBytes())
+ .build();
+ TestUtil.assertAllFieldsSet(message3);
+ }
+ }
+
+ public void testReadMaliciouslyLargeBlob() throws Exception {
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(0x7FFFFFFF);
+ output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
+ output.flush();
+
+ byte[] data = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(data);
+ assertEquals(tag, input.readTag());
+ try {
+ input.readBytes();
+ fail(inputType.name() + ": Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException e) {
+ // success.
+ }
+ }
+ }
+
+ /**
+ * Test we can do messages that are up to CodedInputStream#DEFAULT_SIZE_LIMIT
+ * in size (2G or Integer#MAX_SIZE).
+ * @throws IOException
+ */
+ public void testParseMessagesCloseTo2G() throws IOException {
+ byte[] serializedMessage = getBigSerializedMessage();
+ // How many of these big messages do we need to take us near our 2G limit?
+ int count = Integer.MAX_VALUE / serializedMessage.length;
+ // Now make an inputstream that will fake a near 2G message of messages
+ // returning our big serialized message 'count' times.
+ InputStream is = new RepeatingInputStream(serializedMessage, count);
+ // Parse should succeed!
+ TestAllTypes.parseFrom(is);
+ }
+
+ /**
+ * Test there is an exception if a message exceeds
+ * CodedInputStream#DEFAULT_SIZE_LIMIT in size (2G or Integer#MAX_SIZE).
+ * @throws IOException
+ */
+ public void testParseMessagesOver2G() throws IOException {
+ byte[] serializedMessage = getBigSerializedMessage();
+ // How many of these big messages do we need to take us near our 2G limit?
+ int count = Integer.MAX_VALUE / serializedMessage.length;
+ // Now add one to take us over the limit
+ count++;
+ // Now make an inputstream that will fake a near 2G message of messages
+ // returning our big serialized message 'count' times.
+ InputStream is = new RepeatingInputStream(serializedMessage, count);
+ try {
+ TestAllTypes.parseFrom(is);
+ fail("Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException e) {
+ assertTrue(e.getMessage().contains("too large"));
+ }
+ }
+
+ /*
+ * @return A serialized big message.
+ */
+ private static byte[] getBigSerializedMessage() {
+ byte[] value = new byte[16 * 1024 * 1024];
+ ByteString bsValue = ByteString.wrap(value);
+ return TestAllTypes.newBuilder().setOptionalBytes(bsValue).build().toByteArray();
+ }
+
+ /*
+ * An input stream that repeats a byte arrays' content a number of times.
+ * Simulates really large input without consuming loads of memory. Used above
+ * to test the parsing behavior when the input size exceeds 2G or close to it.
+ */
+ private static class RepeatingInputStream extends InputStream {
+ private final byte[] serializedMessage;
+ private final int count;
+ private int index = 0;
+ private int offset = 0;
+
+ RepeatingInputStream(byte[] serializedMessage, int count) {
+ this.serializedMessage = serializedMessage;
+ this.count = count;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (this.offset == this.serializedMessage.length) {
+ this.index++;
+ this.offset = 0;
+ }
+ if (this.index == this.count) {
+ return -1;
+ }
+ return this.serializedMessage[offset++];
+ }
+ }
+
+ private TestRecursiveMessage makeRecursiveMessage(int depth) {
+ if (depth == 0) {
+ return TestRecursiveMessage.newBuilder().setI(5).build();
+ } else {
+ return TestRecursiveMessage.newBuilder().setA(makeRecursiveMessage(depth - 1)).build();
+ }
+ }
+
+ private void assertMessageDepth(String msg, TestRecursiveMessage message, int depth) {
+ if (depth == 0) {
+ assertFalse(msg, message.hasA());
+ assertEquals(msg, 5, message.getI());
+ } else {
+ assertTrue(msg, message.hasA());
+ assertMessageDepth(msg, message.getA(), depth - 1);
+ }
+ }
+
+ public void testMaliciousRecursion() throws Exception {
+ byte[] data100 = makeRecursiveMessage(100).toByteArray();
+ byte[] data101 = makeRecursiveMessage(101).toByteArray();
+
+ for (InputType inputType : InputType.values()) {
+ assertMessageDepth(
+ inputType.name(), TestRecursiveMessage.parseFrom(inputType.newDecoder(data100)), 100);
+
+ try {
+ TestRecursiveMessage.parseFrom(inputType.newDecoder(data101));
+ fail("Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException e) {
+ // success.
+ }
+
+ CodedInputStream input = inputType.newDecoder(data100);
+ input.setRecursionLimit(8);
+ try {
+ TestRecursiveMessage.parseFrom(input);
+ fail(inputType.name() + ": Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException e) {
+ // success.
+ }
+ }
+ }
+
+ private void checkSizeLimitExceeded(InvalidProtocolBufferException e) {
+ assertEquals(InvalidProtocolBufferException.sizeLimitExceeded().getMessage(), e.getMessage());
+ }
+
+ public void testSizeLimit() throws Exception {
+ // NOTE: Size limit only applies to the stream-backed CIS.
+ CodedInputStream input =
+ CodedInputStream.newInstance(
+ new SmallBlockInputStream(TestUtil.getAllSet().toByteArray(), 16));
+ input.setSizeLimit(16);
+
+ try {
+ TestAllTypes.parseFrom(input);
+ fail("Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException expected) {
+ checkSizeLimitExceeded(expected);
+ }
+ }
+
+ public void testResetSizeCounter() throws Exception {
+ // NOTE: Size limit only applies to the stream-backed CIS.
+ CodedInputStream input =
+ CodedInputStream.newInstance(new SmallBlockInputStream(new byte[256], 8));
+ input.setSizeLimit(16);
+ input.readRawBytes(16);
+ assertEquals(16, input.getTotalBytesRead());
+
+ try {
+ input.readRawByte();
+ fail("Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException expected) {
+ checkSizeLimitExceeded(expected);
+ }
+
+ input.resetSizeCounter();
+ assertEquals(0, input.getTotalBytesRead());
+ input.readRawByte(); // No exception thrown.
+ input.resetSizeCounter();
+ assertEquals(0, input.getTotalBytesRead());
+ input.readRawBytes(16);
+ assertEquals(16, input.getTotalBytesRead());
+ input.resetSizeCounter();
+
+ try {
+ input.readRawBytes(17); // Hits limit again.
+ fail("Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException expected) {
+ checkSizeLimitExceeded(expected);
+ }
+ }
+
+ public void testRefillBufferWithCorrectSize() throws Exception {
+ // NOTE: refillBuffer only applies to the stream-backed CIS.
+ byte[] bytes = "123456789".getBytes("UTF-8");
+ ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.writeRawByte(4);
+ output.flush();
+
+ // Input is two string with length 9 and one raw byte.
+ byte[] rawInput = rawOutput.toByteArray();
+ for (int inputStreamBufferLength = 8;
+ inputStreamBufferLength <= rawInput.length + 1; inputStreamBufferLength++) {
+ CodedInputStream input = CodedInputStream.newInstance(
+ new ByteArrayInputStream(rawInput), inputStreamBufferLength);
+ input.setSizeLimit(rawInput.length - 1);
+ input.readString();
+ input.readString();
+ try {
+ input.readRawByte(); // Hits limit.
+ fail("Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException expected) {
+ checkSizeLimitExceeded(expected);
+ }
+ }
+ }
+
+ public void testIsAtEnd() throws Exception {
+ CodedInputStream input = CodedInputStream.newInstance(
+ new ByteArrayInputStream(new byte[5]));
+ try {
+ for (int i = 0; i < 5; i++) {
+ assertEquals(false, input.isAtEnd());
+ input.readRawByte();
+ }
+ assertEquals(true, input.isAtEnd());
+ } catch (Exception e) {
+ fail("Catch exception in the testIsAtEnd");
+ }
+ }
+
+ public void testCurrentLimitExceeded() throws Exception {
+ byte[] bytes = "123456789".getBytes("UTF-8");
+ ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.flush();
+
+ byte[] rawInput = rawOutput.toByteArray();
+ CodedInputStream input = CodedInputStream.newInstance(
+ new ByteArrayInputStream(rawInput));
+ // The length of the whole rawInput
+ input.setSizeLimit(11);
+ // Some number that is smaller than the rawInput's length
+ // but larger than 2
+ input.pushLimit(5);
+ try {
+ input.readString();
+ fail("Should have thrown an exception");
+ } catch (InvalidProtocolBufferException expected) {
+ assertEquals(expected.getMessage(),
+ InvalidProtocolBufferException.truncatedMessage().getMessage());
+ }
+ }
+
+ public void testSizeLimitMultipleMessages() throws Exception {
+ // NOTE: Size limit only applies to the stream-backed CIS.
+ byte[] bytes = new byte[256];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = (byte) i;
+ }
+ CodedInputStream input = CodedInputStream.newInstance(new SmallBlockInputStream(bytes, 7));
+ input.setSizeLimit(16);
+ for (int i = 0; i < 256 / 16; i++) {
+ byte[] message = input.readRawBytes(16);
+ for (int j = 0; j < message.length; j++) {
+ assertEquals(i * 16 + j, message[j] & 0xff);
+ }
+ assertEquals(16, input.getTotalBytesRead());
+ input.resetSizeCounter();
+ assertEquals(0, input.getTotalBytesRead());
+ }
+ }
+
+ public void testReadString() throws Exception {
+ String lorem = "Lorem ipsum dolor sit amet ";
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < 4096; i += lorem.length()) {
+ builder.append(lorem);
+ }
+ lorem = builder.toString().substring(0, 4096);
+ byte[] bytes = lorem.getBytes("UTF-8");
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.flush();
+
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawInput);
+ assertEquals(inputType.name(), tag, input.readTag());
+ String text = input.readString();
+ assertEquals(inputType.name(), lorem, text);
+ }
+ }
+
+ public void testReadStringRequireUtf8() throws Exception {
+ String lorem = "Lorem ipsum dolor sit amet ";
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < 4096; i += lorem.length()) {
+ builder.append(lorem);
+ }
+ lorem = builder.toString().substring(0, 4096);
+ byte[] bytes = lorem.getBytes("UTF-8");
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.flush();
+
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawInput);
+ assertEquals(inputType.name(), tag, input.readTag());
+ String text = input.readStringRequireUtf8();
+ assertEquals(inputType.name(), lorem, text);
+ }
+ }
+
+ /**
+ * Tests that if we readString invalid UTF-8 bytes, no exception is thrown. Instead, the invalid
+ * bytes are replaced with the Unicode "replacement character" U+FFFD.
+ */
+ public void testReadStringInvalidUtf8() throws Exception {
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(1);
+ output.writeRawBytes(new byte[] {(byte) 0x80});
+ output.flush();
+
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawInput);
+ assertEquals(inputType.name(), tag, input.readTag());
+ String text = input.readString();
+ assertEquals(inputType.name(), 0xfffd, text.charAt(0));
+ }
+ }
+
+ /**
+ * Tests that if we readStringRequireUtf8 invalid UTF-8 bytes, an InvalidProtocolBufferException
+ * is thrown.
+ */
+ public void testReadStringRequireUtf8InvalidUtf8() throws Exception {
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(1);
+ output.writeRawBytes(new byte[] {(byte) 0x80});
+ output.flush();
+
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawInput);
+ assertEquals(tag, input.readTag());
+ try {
+ input.readStringRequireUtf8();
+ fail(inputType.name() + ": Expected invalid UTF-8 exception.");
+ } catch (InvalidProtocolBufferException exception) {
+ assertEquals(
+ inputType.name(), "Protocol message had invalid UTF-8.", exception.getMessage());
+ }
+ }
+ }
+
+ public void testReadFromSlice() throws Exception {
+ byte[] bytes = bytes(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+ CodedInputStream in = CodedInputStream.newInstance(bytes, 3, 5);
+ assertEquals(0, in.getTotalBytesRead());
+ for (int i = 3; i < 8; i++) {
+ assertEquals(i, in.readRawByte());
+ assertEquals(i - 2, in.getTotalBytesRead());
+ }
+ // eof
+ assertEquals(0, in.readTag());
+ assertEquals(5, in.getTotalBytesRead());
+ }
+
+ public void testInvalidTag() throws Exception {
+ // Any tag number which corresponds to field number zero is invalid and
+ // should throw InvalidProtocolBufferException.
+ for (InputType inputType : InputType.values()) {
+ for (int i = 0; i < 8; i++) {
+ try {
+ inputType.newDecoder(bytes(i)).readTag();
+ fail(inputType.name() + ": Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ assertEquals(
+ inputType.name(),
+ InvalidProtocolBufferException.invalidTag().getMessage(),
+ e.getMessage());
+ }
+ }
+ }
+ }
+
+ public void testReadByteArray() throws Exception {
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+ // Zero-sized bytes field.
+ output.writeRawVarint32(0);
+ // One one-byte bytes field
+ output.writeRawVarint32(1);
+ output.writeRawBytes(new byte[] {(byte) 23});
+ // Another one-byte bytes field
+ output.writeRawVarint32(1);
+ output.writeRawBytes(new byte[] {(byte) 45});
+ // A bytes field large enough that won't fit into the 4K buffer.
+ final int bytesLength = 16 * 1024;
+ byte[] bytes = new byte[bytesLength];
+ bytes[0] = (byte) 67;
+ bytes[bytesLength - 1] = (byte) 89;
+ output.writeRawVarint32(bytesLength);
+ output.writeRawBytes(bytes);
+
+ output.flush();
+
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream inputStream = inputType.newDecoder(rawInput);
+
+ byte[] result = inputStream.readByteArray();
+ assertEquals(inputType.name(), 0, result.length);
+ result = inputStream.readByteArray();
+ assertEquals(inputType.name(), 1, result.length);
+ assertEquals(inputType.name(), (byte) 23, result[0]);
+ result = inputStream.readByteArray();
+ assertEquals(inputType.name(), 1, result.length);
+ assertEquals(inputType.name(), (byte) 45, result[0]);
+ result = inputStream.readByteArray();
+ assertEquals(inputType.name(), bytesLength, result.length);
+ assertEquals(inputType.name(), (byte) 67, result[0]);
+ assertEquals(inputType.name(), (byte) 89, result[bytesLength - 1]);
+ }
+ }
+
+ public void testReadLargeByteStringFromInputStream() throws Exception {
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = (byte) (i & 0xFF);
+ }
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.flush();
+ byte[] data = rawOutput.toByteString().toByteArray();
+
+ CodedInputStream input = CodedInputStream.newInstance(
+ new ByteArrayInputStream(data) {
+ @Override
+ public synchronized int available() {
+ return 0;
+ }
+ });
+ ByteString result = input.readBytes();
+ assertEquals(ByteString.copyFrom(bytes), result);
+ }
+
+ public void testReadLargeByteArrayFromInputStream() throws Exception {
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = (byte) (i & 0xFF);
+ }
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.flush();
+ byte[] data = rawOutput.toByteString().toByteArray();
+
+ CodedInputStream input = CodedInputStream.newInstance(
+ new ByteArrayInputStream(data) {
+ @Override
+ public synchronized int available() {
+ return 0;
+ }
+ });
+ byte[] result = input.readByteArray();
+ assertTrue(Arrays.equals(bytes, result));
+ }
+
+ public void testReadByteBuffer() throws Exception {
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+ // Zero-sized bytes field.
+ output.writeRawVarint32(0);
+ // One one-byte bytes field
+ output.writeRawVarint32(1);
+ output.writeRawBytes(new byte[] {(byte) 23});
+ // Another one-byte bytes field
+ output.writeRawVarint32(1);
+ output.writeRawBytes(new byte[] {(byte) 45});
+ // A bytes field large enough that won't fit into the 4K buffer.
+ final int bytesLength = 16 * 1024;
+ byte[] bytes = new byte[bytesLength];
+ bytes[0] = (byte) 67;
+ bytes[bytesLength - 1] = (byte) 89;
+ output.writeRawVarint32(bytesLength);
+ output.writeRawBytes(bytes);
+
+ output.flush();
+
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream inputStream = inputType.newDecoder(rawInput);
+
+ ByteBuffer result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), 0, result.capacity());
+ result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 23, result.get());
+ result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 45, result.get());
+ result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), bytesLength, result.capacity());
+ assertEquals(inputType.name(), (byte) 67, result.get());
+ result.position(bytesLength - 1);
+ assertEquals(inputType.name(), (byte) 89, result.get());
+ }
+ }
+
+ public void testReadByteBufferAliasing() throws Exception {
+ ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(byteArrayStream);
+ // Zero-sized bytes field.
+ output.writeRawVarint32(0);
+ // One one-byte bytes field
+ output.writeRawVarint32(1);
+ output.writeRawBytes(new byte[] {(byte) 23});
+ // Another one-byte bytes field
+ output.writeRawVarint32(1);
+ output.writeRawBytes(new byte[] {(byte) 45});
+ // A bytes field large enough that won't fit into the 4K buffer.
+ final int bytesLength = 16 * 1024;
+ byte[] bytes = new byte[bytesLength];
+ bytes[0] = (byte) 67;
+ bytes[bytesLength - 1] = (byte) 89;
+ output.writeRawVarint32(bytesLength);
+ output.writeRawBytes(bytes);
+ output.flush();
+
+ byte[] data = byteArrayStream.toByteArray();
+
+ for (InputType inputType : InputType.values()) {
+ if (inputType == InputType.STREAM) {
+ // Aliasing doesn't apply to stream-backed CIS.
+ continue;
+ }
+
+ // Without aliasing
+ CodedInputStream inputStream = inputType.newDecoder(data);
+ ByteBuffer result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), 0, result.capacity());
+ result = inputStream.readByteBuffer();
+ assertTrue(inputType.name(), result.array() != data);
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 23, result.get());
+ result = inputStream.readByteBuffer();
+ assertTrue(inputType.name(), result.array() != data);
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 45, result.get());
+ result = inputStream.readByteBuffer();
+ assertTrue(inputType.name(), result.array() != data);
+ assertEquals(inputType.name(), bytesLength, result.capacity());
+ assertEquals(inputType.name(), (byte) 67, result.get());
+ result.position(bytesLength - 1);
+ assertEquals(inputType.name(), (byte) 89, result.get());
+
+ // Enable aliasing
+ inputStream = inputType.newDecoder(data);
+ inputStream.enableAliasing(true);
+ result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), 0, result.capacity());
+ result = inputStream.readByteBuffer();
+ if (result.hasArray()) {
+ assertTrue(inputType.name(), result.array() == data);
+ }
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 23, result.get());
+ result = inputStream.readByteBuffer();
+ if (result.hasArray()) {
+ assertTrue(inputType.name(), result.array() == data);
+ }
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 45, result.get());
+ result = inputStream.readByteBuffer();
+ if (result.hasArray()) {
+ assertTrue(inputType.name(), result.array() == data);
+ }
+ assertEquals(inputType.name(), bytesLength, result.capacity());
+ assertEquals(inputType.name(), (byte) 67, result.get());
+ result.position(bytesLength - 1);
+ assertEquals(inputType.name(), (byte) 89, result.get());
+ }
+ }
+
+ public void testCompatibleTypes() throws Exception {
+ long data = 0x100000000L;
+ Int64Message message = Int64Message.newBuilder().setData(data).build();
+ byte[] serialized = message.toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream inputStream = inputType.newDecoder(serialized);
+
+ // Test int64(long) is compatible with bool(boolean)
+ BoolMessage msg2 = BoolMessage.parseFrom(inputStream);
+ assertTrue(msg2.getData());
+
+ // Test int64(long) is compatible with int32(int)
+ inputStream = inputType.newDecoder(serialized);
+ Int32Message msg3 = Int32Message.parseFrom(inputStream);
+ assertEquals((int) data, msg3.getData());
+ }
+ }
+
+ public void testSkipInvalidVarint_FastPath() throws Exception {
+ // Fast path: We have >= 10 bytes available. Ensure we properly recognize a non-ending varint.
+ byte[] data = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0};
+ for (InputType inputType : InputType.values()) {
+ try {
+ CodedInputStream input = inputType.newDecoder(data);
+ input.skipField(WireFormat.makeTag(1, WireFormat.WIRETYPE_VARINT));
+ fail(inputType.name() + ": Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ // Expected
+ }
+ }
+ }
+
+ public void testSkipInvalidVarint_SlowPath() throws Exception {
+ // Slow path: < 10 bytes available. Ensure we properly recognize a non-ending varint.
+ byte[] data = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1, -1};
+ for (InputType inputType : InputType.values()) {
+ try {
+ CodedInputStream input = inputType.newDecoder(data);
+ input.skipField(WireFormat.makeTag(1, WireFormat.WIRETYPE_VARINT));
+ fail(inputType.name() + ": Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ // Expected
+ }
+ }
+ }
+}