Home | History | Annotate | Download | only in protobuf
      1 // Protocol Buffers - Google's data interchange format
      2 // Copyright 2008 Google Inc.  All rights reserved.
      3 // https://developers.google.com/protocol-buffers/
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are
      7 // met:
      8 //
      9 //     * Redistributions of source code must retain the above copyright
     10 // notice, this list of conditions and the following disclaimer.
     11 //     * Redistributions in binary form must reproduce the above
     12 // copyright notice, this list of conditions and the following disclaimer
     13 // in the documentation and/or other materials provided with the
     14 // distribution.
     15 //     * Neither the name of Google Inc. nor the names of its
     16 // contributors may be used to endorse or promote products derived from
     17 // this software without specific prior written permission.
     18 //
     19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 package com.google.protobuf;
     32 
     33 import protobuf_unittest.UnittestProto.BoolMessage;
     34 import protobuf_unittest.UnittestProto.Int32Message;
     35 import protobuf_unittest.UnittestProto.Int64Message;
     36 import protobuf_unittest.UnittestProto.TestAllTypes;
     37 import protobuf_unittest.UnittestProto.TestRecursiveMessage;
     38 
     39 import junit.framework.TestCase;
     40 
     41 import java.io.ByteArrayInputStream;
     42 import java.io.ByteArrayOutputStream;
     43 import java.io.FilterInputStream;
     44 import java.io.IOException;
     45 import java.io.InputStream;
     46 import java.nio.ByteBuffer;
     47 
     48 /**
     49  * Unit test for {@link CodedInputStream}.
     50  *
     51  * @author kenton (at) google.com Kenton Varda
     52  */
     53 public class CodedInputStreamTest extends TestCase {
     54   /**
     55    * Helper to construct a byte array from a bunch of bytes.  The inputs are
     56    * actually ints so that I can use hex notation and not get stupid errors
     57    * about precision.
     58    */
     59   private byte[] bytes(int... bytesAsInts) {
     60     byte[] bytes = new byte[bytesAsInts.length];
     61     for (int i = 0; i < bytesAsInts.length; i++) {
     62       bytes[i] = (byte) bytesAsInts[i];
     63     }
     64     return bytes;
     65   }
     66 
     67   /**
     68    * An InputStream which limits the number of bytes it reads at a time.
     69    * We use this to make sure that CodedInputStream doesn't screw up when
     70    * reading in small blocks.
     71    */
     72   private static final class SmallBlockInputStream extends FilterInputStream {
     73     private final int blockSize;
     74 
     75     public SmallBlockInputStream(byte[] data, int blockSize) {
     76       this(new ByteArrayInputStream(data), blockSize);
     77     }
     78 
     79     public SmallBlockInputStream(InputStream in, int blockSize) {
     80       super(in);
     81       this.blockSize = blockSize;
     82     }
     83 
     84     public int read(byte[] b) throws IOException {
     85       return super.read(b, 0, Math.min(b.length, blockSize));
     86     }
     87 
     88     public int read(byte[] b, int off, int len) throws IOException {
     89       return super.read(b, off, Math.min(len, blockSize));
     90     }
     91   }
     92 
     93   private void assertDataConsumed(byte[] data, CodedInputStream input)
     94       throws IOException {
     95     assertEquals(data.length, input.getTotalBytesRead());
     96     assertTrue(input.isAtEnd());
     97   }
     98 
     99   /**
    100    * Parses the given bytes using readRawVarint32() and readRawVarint64() and
    101    * checks that the result matches the given value.
    102    */
    103   private void assertReadVarint(byte[] data, long value) throws Exception {
    104     CodedInputStream input = CodedInputStream.newInstance(data);
    105     assertEquals((int) value, input.readRawVarint32());
    106     assertDataConsumed(data, input);
    107 
    108     input = CodedInputStream.newInstance(data);
    109     assertEquals(value, input.readRawVarint64());
    110     assertDataConsumed(data, input);
    111 
    112     input = CodedInputStream.newInstance(data);
    113     assertEquals(value, input.readRawVarint64SlowPath());
    114     assertDataConsumed(data, input);
    115 
    116     input = CodedInputStream.newInstance(data);
    117     assertTrue(input.skipField(WireFormat.WIRETYPE_VARINT));
    118     assertDataConsumed(data, input);
    119 
    120     // Try different block sizes.
    121     for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
    122       input = CodedInputStream.newInstance(
    123         new SmallBlockInputStream(data, blockSize));
    124       assertEquals((int) value, input.readRawVarint32());
    125       assertDataConsumed(data, input);
    126 
    127       input = CodedInputStream.newInstance(
    128         new SmallBlockInputStream(data, blockSize));
    129       assertEquals(value, input.readRawVarint64());
    130       assertDataConsumed(data, input);
    131 
    132       input = CodedInputStream.newInstance(
    133         new SmallBlockInputStream(data, blockSize));
    134       assertEquals(value, input.readRawVarint64SlowPath());
    135       assertDataConsumed(data, input);
    136 
    137       input = CodedInputStream.newInstance(
    138         new SmallBlockInputStream(data, blockSize));
    139       assertTrue(input.skipField(WireFormat.WIRETYPE_VARINT));
    140       assertDataConsumed(data, input);
    141     }
    142 
    143     // Try reading direct from an InputStream.  We want to verify that it
    144     // doesn't read past the end of the input, so we copy to a new, bigger
    145     // array first.
    146     byte[] longerData = new byte[data.length + 1];
    147     System.arraycopy(data, 0, longerData, 0, data.length);
    148     InputStream rawInput = new ByteArrayInputStream(longerData);
    149     assertEquals((int) value, CodedInputStream.readRawVarint32(rawInput));
    150     assertEquals(1, rawInput.available());
    151   }
    152 
    153   /**
    154    * Parses the given bytes using readRawVarint32() and readRawVarint64() and
    155    * expects them to fail with an InvalidProtocolBufferException whose
    156    * description matches the given one.
    157    */
    158   private void assertReadVarintFailure(
    159       InvalidProtocolBufferException expected, byte[] data)
    160       throws Exception {
    161     CodedInputStream input = CodedInputStream.newInstance(data);
    162     try {
    163       input.readRawVarint32();
    164       fail("Should have thrown an exception.");
    165     } catch (InvalidProtocolBufferException e) {
    166       assertEquals(expected.getMessage(), e.getMessage());
    167     }
    168 
    169     input = CodedInputStream.newInstance(data);
    170     try {
    171       input.readRawVarint64();
    172       fail("Should have thrown an exception.");
    173     } catch (InvalidProtocolBufferException e) {
    174       assertEquals(expected.getMessage(), e.getMessage());
    175     }
    176 
    177     input = CodedInputStream.newInstance(data);
    178     try {
    179       input.readRawVarint64SlowPath();
    180       fail("Should have thrown an exception.");
    181     } catch (InvalidProtocolBufferException e) {
    182       assertEquals(expected.getMessage(), e.getMessage());
    183     }
    184 
    185     // Make sure we get the same error when reading direct from an InputStream.
    186     try {
    187       CodedInputStream.readRawVarint32(new ByteArrayInputStream(data));
    188       fail("Should have thrown an exception.");
    189     } catch (InvalidProtocolBufferException e) {
    190       assertEquals(expected.getMessage(), e.getMessage());
    191     }
    192   }
    193 
    194   /** Tests readRawVarint32() and readRawVarint64(). */
    195   public void testReadVarint() throws Exception {
    196     assertReadVarint(bytes(0x00), 0);
    197     assertReadVarint(bytes(0x01), 1);
    198     assertReadVarint(bytes(0x7f), 127);
    199     // 14882
    200     assertReadVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
    201     // 2961488830
    202     assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
    203       (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
    204       (0x0bL << 28));
    205 
    206     // 64-bit
    207     // 7256456126
    208     assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
    209       (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
    210       (0x1bL << 28));
    211     // 41256202580718336
    212     assertReadVarint(
    213       bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
    214       (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
    215       (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
    216     // 11964378330978735131
    217     assertReadVarint(
    218       bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
    219       (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
    220       (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
    221       (0x05L << 49) | (0x26L << 56) | (0x01L << 63));
    222 
    223     // Failures
    224     assertReadVarintFailure(
    225       InvalidProtocolBufferException.malformedVarint(),
    226       bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    227             0x00));
    228     assertReadVarintFailure(
    229       InvalidProtocolBufferException.truncatedMessage(),
    230       bytes(0x80));
    231   }
    232 
    233   /**
    234    * Parses the given bytes using readRawLittleEndian32() and checks
    235    * that the result matches the given value.
    236    */
    237   private void assertReadLittleEndian32(byte[] data, int value)
    238                                         throws Exception {
    239     CodedInputStream input = CodedInputStream.newInstance(data);
    240     assertEquals(value, input.readRawLittleEndian32());
    241     assertTrue(input.isAtEnd());
    242 
    243     // Try different block sizes.
    244     for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
    245       input = CodedInputStream.newInstance(
    246         new SmallBlockInputStream(data, blockSize));
    247       assertEquals(value, input.readRawLittleEndian32());
    248       assertTrue(input.isAtEnd());
    249     }
    250   }
    251 
    252   /**
    253    * Parses the given bytes using readRawLittleEndian64() and checks
    254    * that the result matches the given value.
    255    */
    256   private void assertReadLittleEndian64(byte[] data, long value)
    257                                         throws Exception {
    258     CodedInputStream input = CodedInputStream.newInstance(data);
    259     assertEquals(value, input.readRawLittleEndian64());
    260     assertTrue(input.isAtEnd());
    261 
    262     // Try different block sizes.
    263     for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
    264       input = CodedInputStream.newInstance(
    265         new SmallBlockInputStream(data, blockSize));
    266       assertEquals(value, input.readRawLittleEndian64());
    267       assertTrue(input.isAtEnd());
    268     }
    269   }
    270 
    271   /** Tests readRawLittleEndian32() and readRawLittleEndian64(). */
    272   public void testReadLittleEndian() throws Exception {
    273     assertReadLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
    274     assertReadLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
    275 
    276     assertReadLittleEndian64(
    277       bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
    278       0x123456789abcdef0L);
    279     assertReadLittleEndian64(
    280       bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a),
    281       0x9abcdef012345678L);
    282   }
    283 
    284   /** Test decodeZigZag32() and decodeZigZag64(). */
    285   public void testDecodeZigZag() throws Exception {
    286     assertEquals( 0, CodedInputStream.decodeZigZag32(0));
    287     assertEquals(-1, CodedInputStream.decodeZigZag32(1));
    288     assertEquals( 1, CodedInputStream.decodeZigZag32(2));
    289     assertEquals(-2, CodedInputStream.decodeZigZag32(3));
    290     assertEquals(0x3FFFFFFF, CodedInputStream.decodeZigZag32(0x7FFFFFFE));
    291     assertEquals(0xC0000000, CodedInputStream.decodeZigZag32(0x7FFFFFFF));
    292     assertEquals(0x7FFFFFFF, CodedInputStream.decodeZigZag32(0xFFFFFFFE));
    293     assertEquals(0x80000000, CodedInputStream.decodeZigZag32(0xFFFFFFFF));
    294 
    295     assertEquals( 0, CodedInputStream.decodeZigZag64(0));
    296     assertEquals(-1, CodedInputStream.decodeZigZag64(1));
    297     assertEquals( 1, CodedInputStream.decodeZigZag64(2));
    298     assertEquals(-2, CodedInputStream.decodeZigZag64(3));
    299     assertEquals(0x000000003FFFFFFFL,
    300                  CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL));
    301     assertEquals(0xFFFFFFFFC0000000L,
    302                  CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL));
    303     assertEquals(0x000000007FFFFFFFL,
    304                  CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL));
    305     assertEquals(0xFFFFFFFF80000000L,
    306                  CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL));
    307     assertEquals(0x7FFFFFFFFFFFFFFFL,
    308                  CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL));
    309     assertEquals(0x8000000000000000L,
    310                  CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL));
    311   }
    312 
    313   /** Tests reading and parsing a whole message with every field type. */
    314   public void testReadWholeMessage() throws Exception {
    315     TestAllTypes message = TestUtil.getAllSet();
    316 
    317     byte[] rawBytes = message.toByteArray();
    318     assertEquals(rawBytes.length, message.getSerializedSize());
    319 
    320     TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
    321     TestUtil.assertAllFieldsSet(message2);
    322 
    323     // Try different block sizes.
    324     for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
    325       message2 = TestAllTypes.parseFrom(
    326         new SmallBlockInputStream(rawBytes, blockSize));
    327       TestUtil.assertAllFieldsSet(message2);
    328     }
    329   }
    330 
    331   /** Tests skipField(). */
    332   public void testSkipWholeMessage() throws Exception {
    333     TestAllTypes message = TestUtil.getAllSet();
    334     byte[] rawBytes = message.toByteArray();
    335 
    336     // Create two parallel inputs.  Parse one as unknown fields while using
    337     // skipField() to skip each field on the other.  Expect the same tags.
    338     CodedInputStream input1 = CodedInputStream.newInstance(rawBytes);
    339     CodedInputStream input2 = CodedInputStream.newInstance(rawBytes);
    340     UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder();
    341 
    342     while (true) {
    343       int tag = input1.readTag();
    344       assertEquals(tag, input2.readTag());
    345       if (tag == 0) {
    346         break;
    347       }
    348       unknownFields.mergeFieldFrom(tag, input1);
    349       input2.skipField(tag);
    350     }
    351   }
    352 
    353 
    354   /**
    355    * Test that a bug in skipRawBytes() has been fixed:  if the skip skips
    356    * exactly up to a limit, this should not break things.
    357    */
    358   public void testSkipRawBytesBug() throws Exception {
    359     byte[] rawBytes = new byte[] { 1, 2 };
    360     CodedInputStream input = CodedInputStream.newInstance(rawBytes);
    361 
    362     int limit = input.pushLimit(1);
    363     input.skipRawBytes(1);
    364     input.popLimit(limit);
    365     assertEquals(2, input.readRawByte());
    366   }
    367 
    368   /**
    369    * Test that a bug in skipRawBytes() has been fixed:  if the skip skips
    370    * past the end of a buffer with a limit that has been set past the end of
    371    * that buffer, this should not break things.
    372    */
    373   public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception {
    374     byte[] rawBytes = new byte[] { 1, 2, 3, 4, 5 };
    375     CodedInputStream input = CodedInputStream.newInstance(
    376         new SmallBlockInputStream(rawBytes, 3));
    377 
    378     int limit = input.pushLimit(4);
    379     // In order to expose the bug we need to read at least one byte to prime the
    380     // buffer inside the CodedInputStream.
    381     assertEquals(1, input.readRawByte());
    382     // Skip to the end of the limit.
    383     input.skipRawBytes(3);
    384     assertTrue(input.isAtEnd());
    385     input.popLimit(limit);
    386     assertEquals(5, input.readRawByte());
    387   }
    388 
    389   public void testReadHugeBlob() throws Exception {
    390     // Allocate and initialize a 1MB blob.
    391     byte[] blob = new byte[1 << 20];
    392     for (int i = 0; i < blob.length; i++) {
    393       blob[i] = (byte) i;
    394     }
    395 
    396     // Make a message containing it.
    397     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
    398     TestUtil.setAllFields(builder);
    399     builder.setOptionalBytes(ByteString.copyFrom(blob));
    400     TestAllTypes message = builder.build();
    401 
    402     // Serialize and parse it.  Make sure to parse from an InputStream, not
    403     // directly from a ByteString, so that CodedInputStream uses buffered
    404     // reading.
    405     TestAllTypes message2 =
    406       TestAllTypes.parseFrom(message.toByteString().newInput());
    407 
    408     assertEquals(message.getOptionalBytes(), message2.getOptionalBytes());
    409 
    410     // Make sure all the other fields were parsed correctly.
    411     TestAllTypes message3 = TestAllTypes.newBuilder(message2)
    412       .setOptionalBytes(TestUtil.getAllSet().getOptionalBytes())
    413       .build();
    414     TestUtil.assertAllFieldsSet(message3);
    415   }
    416 
    417   public void testReadMaliciouslyLargeBlob() throws Exception {
    418     ByteString.Output rawOutput = ByteString.newOutput();
    419     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
    420 
    421     int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
    422     output.writeRawVarint32(tag);
    423     output.writeRawVarint32(0x7FFFFFFF);
    424     output.writeRawBytes(new byte[32]);  // Pad with a few random bytes.
    425     output.flush();
    426 
    427     CodedInputStream input = rawOutput.toByteString().newCodedInput();
    428     assertEquals(tag, input.readTag());
    429 
    430     try {
    431       input.readBytes();
    432       fail("Should have thrown an exception!");
    433     } catch (InvalidProtocolBufferException e) {
    434       // success.
    435     }
    436   }
    437 
    438   private TestRecursiveMessage makeRecursiveMessage(int depth) {
    439     if (depth == 0) {
    440       return TestRecursiveMessage.newBuilder().setI(5).build();
    441     } else {
    442       return TestRecursiveMessage.newBuilder()
    443         .setA(makeRecursiveMessage(depth - 1)).build();
    444     }
    445   }
    446 
    447   private void assertMessageDepth(TestRecursiveMessage message, int depth) {
    448     if (depth == 0) {
    449       assertFalse(message.hasA());
    450       assertEquals(5, message.getI());
    451     } else {
    452       assertTrue(message.hasA());
    453       assertMessageDepth(message.getA(), depth - 1);
    454     }
    455   }
    456 
    457   public void testMaliciousRecursion() throws Exception {
    458     ByteString data64 = makeRecursiveMessage(64).toByteString();
    459     ByteString data65 = makeRecursiveMessage(65).toByteString();
    460 
    461     assertMessageDepth(TestRecursiveMessage.parseFrom(data64), 64);
    462 
    463     try {
    464       TestRecursiveMessage.parseFrom(data65);
    465       fail("Should have thrown an exception!");
    466     } catch (InvalidProtocolBufferException e) {
    467       // success.
    468     }
    469 
    470     CodedInputStream input = data64.newCodedInput();
    471     input.setRecursionLimit(8);
    472     try {
    473       TestRecursiveMessage.parseFrom(input);
    474       fail("Should have thrown an exception!");
    475     } catch (InvalidProtocolBufferException e) {
    476       // success.
    477     }
    478   }
    479 
    480   private void checkSizeLimitExceeded(InvalidProtocolBufferException e) {
    481       assertEquals(
    482           InvalidProtocolBufferException.sizeLimitExceeded().getMessage(),
    483           e.getMessage());
    484   }
    485 
    486   public void testSizeLimit() throws Exception {
    487     CodedInputStream input = CodedInputStream.newInstance(
    488         new SmallBlockInputStream(
    489             TestUtil.getAllSet().toByteString().newInput(), 16));
    490     input.setSizeLimit(16);
    491 
    492     try {
    493       TestAllTypes.parseFrom(input);
    494       fail("Should have thrown an exception!");
    495     } catch (InvalidProtocolBufferException expected) {
    496       checkSizeLimitExceeded(expected);
    497     }
    498   }
    499 
    500   public void testResetSizeCounter() throws Exception {
    501     CodedInputStream input = CodedInputStream.newInstance(
    502         new SmallBlockInputStream(new byte[256], 8));
    503     input.setSizeLimit(16);
    504     input.readRawBytes(16);
    505     assertEquals(16, input.getTotalBytesRead());
    506 
    507     try {
    508       input.readRawByte();
    509       fail("Should have thrown an exception!");
    510     } catch (InvalidProtocolBufferException expected) {
    511       checkSizeLimitExceeded(expected);
    512     }
    513 
    514     input.resetSizeCounter();
    515     assertEquals(0, input.getTotalBytesRead());
    516     input.readRawByte();  // No exception thrown.
    517     input.resetSizeCounter();
    518     assertEquals(0, input.getTotalBytesRead());
    519     input.readRawBytes(16);
    520     assertEquals(16, input.getTotalBytesRead());
    521     input.resetSizeCounter();
    522 
    523     try {
    524       input.readRawBytes(17);  // Hits limit again.
    525       fail("Should have thrown an exception!");
    526     } catch (InvalidProtocolBufferException expected) {
    527       checkSizeLimitExceeded(expected);
    528     }
    529   }
    530 
    531   public void testSizeLimitMultipleMessages() throws Exception {
    532     byte[] bytes = new byte[256];
    533     for (int i = 0; i < bytes.length; i++) {
    534       bytes[i] = (byte) i;
    535     }
    536     CodedInputStream input = CodedInputStream.newInstance(
    537         new SmallBlockInputStream(bytes, 7));
    538     input.setSizeLimit(16);
    539     for (int i = 0; i < 256 / 16; i++) {
    540       byte[] message = input.readRawBytes(16);
    541       for (int j = 0; j < message.length; j++) {
    542         assertEquals(i * 16 + j, message[j] & 0xff);
    543       }
    544       assertEquals(16, input.getTotalBytesRead());
    545       input.resetSizeCounter();
    546       assertEquals(0, input.getTotalBytesRead());
    547     }
    548   }
    549 
    550   /**
    551    * Tests that if we readString invalid UTF-8 bytes, no exception
    552    * is thrown.  Instead, the invalid bytes are replaced with the Unicode
    553    * "replacement character" U+FFFD.
    554    */
    555   public void testReadStringInvalidUtf8() throws Exception {
    556     ByteString.Output rawOutput = ByteString.newOutput();
    557     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
    558 
    559     int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
    560     output.writeRawVarint32(tag);
    561     output.writeRawVarint32(1);
    562     output.writeRawBytes(new byte[] { (byte) 0x80 });
    563     output.flush();
    564 
    565     CodedInputStream input = rawOutput.toByteString().newCodedInput();
    566     assertEquals(tag, input.readTag());
    567     String text = input.readString();
    568     assertEquals(0xfffd, text.charAt(0));
    569   }
    570 
    571   /**
    572    * Tests that if we readStringRequireUtf8 invalid UTF-8 bytes, an
    573    * InvalidProtocolBufferException is thrown.
    574    */
    575   public void testReadStringRequireUtf8InvalidUtf8() throws Exception {
    576     ByteString.Output rawOutput = ByteString.newOutput();
    577     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
    578 
    579     int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
    580     output.writeRawVarint32(tag);
    581     output.writeRawVarint32(1);
    582     output.writeRawBytes(new byte[] { (byte) 0x80 });
    583     output.flush();
    584 
    585     CodedInputStream input = rawOutput.toByteString().newCodedInput();
    586     assertEquals(tag, input.readTag());
    587     try {
    588       input.readStringRequireUtf8();
    589       fail("Expected invalid UTF-8 exception.");
    590     } catch (InvalidProtocolBufferException exception) {
    591       assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
    592     }
    593   }
    594 
    595   public void testReadFromSlice() throws Exception {
    596     byte[] bytes = bytes(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
    597     CodedInputStream in = CodedInputStream.newInstance(bytes, 3, 5);
    598     assertEquals(0, in.getTotalBytesRead());
    599     for (int i = 3; i < 8; i++) {
    600       assertEquals(i, in.readRawByte());
    601       assertEquals(i - 2, in.getTotalBytesRead());
    602     }
    603     // eof
    604     assertEquals(0, in.readTag());
    605     assertEquals(5, in.getTotalBytesRead());
    606   }
    607 
    608   public void testInvalidTag() throws Exception {
    609     // Any tag number which corresponds to field number zero is invalid and
    610     // should throw InvalidProtocolBufferException.
    611     for (int i = 0; i < 8; i++) {
    612       try {
    613         CodedInputStream.newInstance(bytes(i)).readTag();
    614         fail("Should have thrown an exception.");
    615       } catch (InvalidProtocolBufferException e) {
    616         assertEquals(InvalidProtocolBufferException.invalidTag().getMessage(),
    617                      e.getMessage());
    618       }
    619     }
    620   }
    621 
    622   public void testReadByteArray() throws Exception {
    623     ByteString.Output rawOutput = ByteString.newOutput();
    624     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
    625     // Zero-sized bytes field.
    626     output.writeRawVarint32(0);
    627     // One one-byte bytes field
    628     output.writeRawVarint32(1);
    629     output.writeRawBytes(new byte[] { (byte) 23 });
    630     // Another one-byte bytes field
    631     output.writeRawVarint32(1);
    632     output.writeRawBytes(new byte[] { (byte) 45 });
    633     // A bytes field large enough that won't fit into the 4K buffer.
    634     final int bytesLength = 16 * 1024;
    635     byte[] bytes = new byte[bytesLength];
    636     bytes[0] = (byte) 67;
    637     bytes[bytesLength - 1] = (byte) 89;
    638     output.writeRawVarint32(bytesLength);
    639     output.writeRawBytes(bytes);
    640 
    641     output.flush();
    642     CodedInputStream inputStream = rawOutput.toByteString().newCodedInput();
    643 
    644     byte[] result = inputStream.readByteArray();
    645     assertEquals(0, result.length);
    646     result = inputStream.readByteArray();
    647     assertEquals(1, result.length);
    648     assertEquals((byte) 23, result[0]);
    649     result = inputStream.readByteArray();
    650     assertEquals(1, result.length);
    651     assertEquals((byte) 45, result[0]);
    652     result = inputStream.readByteArray();
    653     assertEquals(bytesLength, result.length);
    654     assertEquals((byte) 67, result[0]);
    655     assertEquals((byte) 89, result[bytesLength - 1]);
    656   }
    657 
    658   public void testReadByteBuffer() throws Exception {
    659     ByteString.Output rawOutput = ByteString.newOutput();
    660     CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
    661     // Zero-sized bytes field.
    662     output.writeRawVarint32(0);
    663     // One one-byte bytes field
    664     output.writeRawVarint32(1);
    665     output.writeRawBytes(new byte[]{(byte) 23});
    666     // Another one-byte bytes field
    667     output.writeRawVarint32(1);
    668     output.writeRawBytes(new byte[]{(byte) 45});
    669     // A bytes field large enough that won't fit into the 4K buffer.
    670     final int bytesLength = 16 * 1024;
    671     byte[] bytes = new byte[bytesLength];
    672     bytes[0] = (byte) 67;
    673     bytes[bytesLength - 1] = (byte) 89;
    674     output.writeRawVarint32(bytesLength);
    675     output.writeRawBytes(bytes);
    676 
    677     output.flush();
    678     CodedInputStream inputStream = rawOutput.toByteString().newCodedInput();
    679 
    680     ByteBuffer result = inputStream.readByteBuffer();
    681     assertEquals(0, result.capacity());
    682     result = inputStream.readByteBuffer();
    683     assertEquals(1, result.capacity());
    684     assertEquals((byte) 23, result.get());
    685     result = inputStream.readByteBuffer();
    686     assertEquals(1, result.capacity());
    687     assertEquals((byte) 45, result.get());
    688     result = inputStream.readByteBuffer();
    689     assertEquals(bytesLength, result.capacity());
    690     assertEquals((byte) 67, result.get());
    691     result.position(bytesLength - 1);
    692     assertEquals((byte) 89, result.get());
    693   }
    694 
    695   public void testReadByteBufferAliasing() throws Exception {
    696     ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
    697     CodedOutputStream output = CodedOutputStream.newInstance(byteArrayStream);
    698     // Zero-sized bytes field.
    699     output.writeRawVarint32(0);
    700     // One one-byte bytes field
    701     output.writeRawVarint32(1);
    702     output.writeRawBytes(new byte[]{(byte) 23});
    703     // Another one-byte bytes field
    704     output.writeRawVarint32(1);
    705     output.writeRawBytes(new byte[]{(byte) 45});
    706     // A bytes field large enough that won't fit into the 4K buffer.
    707     final int bytesLength = 16 * 1024;
    708     byte[] bytes = new byte[bytesLength];
    709     bytes[0] = (byte) 67;
    710     bytes[bytesLength - 1] = (byte) 89;
    711     output.writeRawVarint32(bytesLength);
    712     output.writeRawBytes(bytes);
    713     output.flush();
    714     byte[] data = byteArrayStream.toByteArray();
    715 
    716     // Without aliasing
    717     CodedInputStream inputStream = CodedInputStream.newInstance(data);
    718     ByteBuffer result = inputStream.readByteBuffer();
    719     assertEquals(0, result.capacity());
    720     result = inputStream.readByteBuffer();
    721     assertTrue(result.array() != data);
    722     assertEquals(1, result.capacity());
    723     assertEquals((byte) 23, result.get());
    724     result = inputStream.readByteBuffer();
    725     assertTrue(result.array() != data);
    726     assertEquals(1, result.capacity());
    727     assertEquals((byte) 45, result.get());
    728     result = inputStream.readByteBuffer();
    729     assertTrue(result.array() != data);
    730     assertEquals(bytesLength, result.capacity());
    731     assertEquals((byte) 67, result.get());
    732     result.position(bytesLength - 1);
    733     assertEquals((byte) 89, result.get());
    734 
    735     // Enable aliasing
    736     inputStream = CodedInputStream.newInstance(data);
    737     inputStream.enableAliasing(true);
    738     result = inputStream.readByteBuffer();
    739     assertEquals(0, result.capacity());
    740     result = inputStream.readByteBuffer();
    741     assertTrue(result.array() == data);
    742     assertEquals(1, result.capacity());
    743     assertEquals((byte) 23, result.get());
    744     result = inputStream.readByteBuffer();
    745     assertTrue(result.array() == data);
    746     assertEquals(1, result.capacity());
    747     assertEquals((byte) 45, result.get());
    748     result = inputStream.readByteBuffer();
    749     assertTrue(result.array() == data);
    750     assertEquals(bytesLength, result.capacity());
    751     assertEquals((byte) 67, result.get());
    752     result.position(bytesLength - 1);
    753     assertEquals((byte) 89, result.get());
    754   }
    755 
    756   public void testCompatibleTypes() throws Exception {
    757     long data = 0x100000000L;
    758     Int64Message message = Int64Message.newBuilder().setData(data).build();
    759     ByteString serialized = message.toByteString();
    760 
    761     // Test int64(long) is compatible with bool(boolean)
    762     BoolMessage msg2 = BoolMessage.parseFrom(serialized);
    763     assertTrue(msg2.getData());
    764 
    765     // Test int64(long) is compatible with int32(int)
    766     Int32Message msg3 = Int32Message.parseFrom(serialized);
    767     assertEquals((int) data, msg3.getData());
    768   }
    769 }
    770