public byte [ ] readRawBytes ( final int size ) throws IOException { if ( size < 0 ) { throw ProtobufException . negativeSize ( ) ; } if ( totalBytesRetired + bufferPos + size > currentLimit ) { // Read to the end of the stream anyway. skipRawBytes ( currentLimit - totalBytesRetired - bufferPos ) ; // Then fail. throw ProtobufException . truncatedMessage ( ) ; } if ( size <= bufferSize - bufferPos ) { // We have all the bytes we need already. final byte [ ] bytes = new byte [ size ] ; System . arraycopy ( buffer , bufferPos , bytes , 0 , size ) ; bufferPos += size ; return bytes ; } else if ( size < buffer . length ) { // Reading more bytes than are in the buffer, but not an excessive number // of bytes. We can safely allocate the resulting array ahead of time. // First copy what we have. final byte [ ] bytes = new byte [ size ] ; int pos = bufferSize - bufferPos ; System . arraycopy ( buffer , bufferPos , bytes , 0 , pos ) ; bufferPos = bufferSize ; // We want to use refillBuffer() and then copy from the buffer into our // byte array rather than reading directly into our byte array because // the input may be unbuffered. refillBuffer ( true ) ; while ( size - pos > bufferSize ) { System . arraycopy ( buffer , 0 , bytes , pos , bufferSize ) ; pos += bufferSize ; bufferPos = bufferSize ; refillBuffer ( true ) ; } System . arraycopy ( buffer , 0 , bytes , pos , size - pos ) ; bufferPos = size - pos ; return bytes ; } else { // The size is very large. For security reasons, we can't allocate the // entire byte array yet. The size comes directly from the input, so a // maliciously-crafted message could provide a bogus very large size in // order to trick the app into allocating a lot of memory. We avoid this // by allocating and reading only a small chunk at a time, so that the // malicious message must actually *be* extremely large to cause // problems. Meanwhile, we limit the allowed size of a message elsewhere. // Remember the buffer markers since we'll have to copy the bytes out of // it later. final int originalBufferPos = bufferPos ; final int originalBufferSize = bufferSize ; // Mark the current buffer consumed. totalBytesRetired += bufferSize ; bufferPos = 0 ; bufferSize = 0 ; // Read all the rest of the bytes we need. int sizeLeft = size - ( originalBufferSize - originalBufferPos ) ; final List < byte [ ] > chunks = new ArrayList < byte [ ] > ( ) ; while ( sizeLeft > 0 ) { final byte [ ] chunk = new byte [ Math . min ( sizeLeft , buffer . length ) ] ; int pos = 0 ; while ( pos < chunk . length ) { final int n = ( input == null ) ? - 1 : input . read ( chunk , pos , chunk . length - pos ) ; if ( n == - 1 ) { throw ProtobufException . truncatedMessage ( ) ; } totalBytesRetired += n ; pos += n ; } sizeLeft -= chunk . length ; chunks . add ( chunk ) ; } // OK, got everything. Now concatenate it all into one buffer. final byte [ ] bytes = new byte [ size ] ; // Start by copying the leftover bytes from this.buffer. int pos = originalBufferSize - originalBufferPos ; System . arraycopy ( buffer , originalBufferPos , bytes , 0 , pos ) ; // And now all the chunks. for ( final byte [ ] chunk : chunks ) { System . arraycopy ( chunk , 0 , bytes , pos , chunk . length ) ; pos += chunk . length ; } // Done. return bytes ; } }
Read a fixed size of bytes from the input .