• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // http://code.google.com/p/protobuf/
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.micro;
32 
33 import java.io.IOException;
34 import java.io.InputStream;
35 
36 /**
37  * Reads and decodes protocol message fields.
38  *
39  * This class contains two kinds of methods:  methods that read specific
40  * protocol message constructs and field types (e.g. {@link #readTag()} and
41  * {@link #readInt32()}) and methods that read low-level values (e.g.
42  * {@link #readRawVarint32()} and {@link #readRawBytes}).  If you are reading
43  * encoded protocol messages, you should use the former methods, but if you are
44  * reading some other format of your own design, use the latter.
45  *
46  * @author kenton@google.com Kenton Varda
47  */
48 public final class CodedInputStreamMicro {
49   /**
50    * Create a new CodedInputStream wrapping the given InputStream.
51    */
newInstance(final InputStream input)52   public static CodedInputStreamMicro newInstance(final InputStream input) {
53     return new CodedInputStreamMicro(input);
54   }
55 
56   /**
57    * Create a new CodedInputStream wrapping the given byte array.
58    */
newInstance(final byte[] buf)59   public static CodedInputStreamMicro newInstance(final byte[] buf) {
60     return newInstance(buf, 0, buf.length);
61   }
62 
63   /**
64    * Create a new CodedInputStream wrapping the given byte array slice.
65    */
newInstance(final byte[] buf, final int off, final int len)66   public static CodedInputStreamMicro newInstance(final byte[] buf, final int off,
67                                              final int len) {
68     return new CodedInputStreamMicro(buf, off, len);
69   }
70 
71   // -----------------------------------------------------------------
72 
73   /**
74    * Attempt to read a field tag, returning zero if we have reached EOF.
75    * Protocol message parsers use this to read tags, since a protocol message
76    * may legally end wherever a tag occurs, and zero is not a valid tag number.
77    */
readTag()78   public int readTag() throws IOException {
79     if (isAtEnd()) {
80       lastTag = 0;
81       return 0;
82     }
83 
84     lastTag = readRawVarint32();
85     if (lastTag == 0) {
86       // If we actually read zero, that's not a valid tag.
87       throw InvalidProtocolBufferMicroException.invalidTag();
88     }
89     return lastTag;
90   }
91 
92   /**
93    * Verifies that the last call to readTag() returned the given tag value.
94    * This is used to verify that a nested group ended with the correct
95    * end tag.
96    *
97    * @throws InvalidProtocolBufferMicroException {@code value} does not match the
98    *                                        last tag.
99    */
checkLastTagWas(final int value)100   public void checkLastTagWas(final int value)
101                               throws InvalidProtocolBufferMicroException {
102     if (lastTag != value) {
103       throw InvalidProtocolBufferMicroException.invalidEndTag();
104     }
105   }
106 
107   /**
108    * Reads and discards a single field, given its tag value.
109    *
110    * @return {@code false} if the tag is an endgroup tag, in which case
111    *         nothing is skipped.  Otherwise, returns {@code true}.
112    */
skipField(final int tag)113   public boolean skipField(final int tag) throws IOException {
114     switch (WireFormatMicro.getTagWireType(tag)) {
115       case WireFormatMicro.WIRETYPE_VARINT:
116         readInt32();
117         return true;
118       case WireFormatMicro.WIRETYPE_FIXED64:
119         readRawLittleEndian64();
120         return true;
121       case WireFormatMicro.WIRETYPE_LENGTH_DELIMITED:
122         skipRawBytes(readRawVarint32());
123         return true;
124       case WireFormatMicro.WIRETYPE_START_GROUP:
125         skipMessage();
126         checkLastTagWas(
127           WireFormatMicro.makeTag(WireFormatMicro.getTagFieldNumber(tag),
128                              WireFormatMicro.WIRETYPE_END_GROUP));
129         return true;
130       case WireFormatMicro.WIRETYPE_END_GROUP:
131         return false;
132       case WireFormatMicro.WIRETYPE_FIXED32:
133         readRawLittleEndian32();
134         return true;
135       default:
136         throw InvalidProtocolBufferMicroException.invalidWireType();
137     }
138   }
139 
140   /**
141    * Reads and discards an entire message.  This will read either until EOF
142    * or until an endgroup tag, whichever comes first.
143    */
skipMessage()144   public void skipMessage() throws IOException {
145     while (true) {
146       final int tag = readTag();
147       if (tag == 0 || !skipField(tag)) {
148         return;
149       }
150     }
151   }
152 
153   // -----------------------------------------------------------------
154 
155   /** Read a {@code double} field value from the stream. */
readDouble()156   public double readDouble() throws IOException {
157     return Double.longBitsToDouble(readRawLittleEndian64());
158   }
159 
160   /** Read a {@code float} field value from the stream. */
readFloat()161   public float readFloat() throws IOException {
162     return Float.intBitsToFloat(readRawLittleEndian32());
163   }
164 
165   /** Read a {@code uint64} field value from the stream. */
readUInt64()166   public long readUInt64() throws IOException {
167     return readRawVarint64();
168   }
169 
170   /** Read an {@code int64} field value from the stream. */
readInt64()171   public long readInt64() throws IOException {
172     return readRawVarint64();
173   }
174 
175   /** Read an {@code int32} field value from the stream. */
readInt32()176   public int readInt32() throws IOException {
177     return readRawVarint32();
178   }
179 
180   /** Read a {@code fixed64} field value from the stream. */
readFixed64()181   public long readFixed64() throws IOException {
182     return readRawLittleEndian64();
183   }
184 
185   /** Read a {@code fixed32} field value from the stream. */
readFixed32()186   public int readFixed32() throws IOException {
187     return readRawLittleEndian32();
188   }
189 
190   /** Read a {@code bool} field value from the stream. */
readBool()191   public boolean readBool() throws IOException {
192     return readRawVarint32() != 0;
193   }
194 
195   /** Read a {@code string} field value from the stream. */
readString()196   public String readString() throws IOException {
197     final int size = readRawVarint32();
198     if (size <= (bufferSize - bufferPos) && size > 0) {
199       // Fast path:  We already have the bytes in a contiguous buffer, so
200       //   just copy directly from it.
201       final String result = new String(buffer, bufferPos, size, "UTF-8");
202       bufferPos += size;
203       return result;
204     } else {
205       // Slow path:  Build a byte array first then copy it.
206       return new String(readRawBytes(size), "UTF-8");
207     }
208   }
209 
210   /** Read a {@code group} field value from the stream. */
readGroup(final MessageMicro msg, final int fieldNumber)211   public void readGroup(final MessageMicro msg, final int fieldNumber)
212       throws IOException {
213     if (recursionDepth >= recursionLimit) {
214       throw InvalidProtocolBufferMicroException.recursionLimitExceeded();
215     }
216     ++recursionDepth;
217     msg.mergeFrom(this);
218     checkLastTagWas(
219       WireFormatMicro.makeTag(fieldNumber, WireFormatMicro.WIRETYPE_END_GROUP));
220     --recursionDepth;
221   }
222 
readMessage(final MessageMicro msg)223   public void readMessage(final MessageMicro msg)
224       throws IOException {
225     final int length = readRawVarint32();
226     if (recursionDepth >= recursionLimit) {
227       throw InvalidProtocolBufferMicroException.recursionLimitExceeded();
228     }
229     final int oldLimit = pushLimit(length);
230     ++recursionDepth;
231     msg.mergeFrom(this);
232     checkLastTagWas(0);
233     --recursionDepth;
234     popLimit(oldLimit);
235   }
236 
237   /** Read a {@code bytes} field value from the stream. */
readBytes()238   public ByteStringMicro readBytes() throws IOException {
239     final int size = readRawVarint32();
240     if (size <= (bufferSize - bufferPos) && size > 0) {
241       // Fast path:  We already have the bytes in a contiguous buffer, so
242       //   just copy directly from it.
243       final ByteStringMicro result = ByteStringMicro.copyFrom(buffer, bufferPos, size);
244       bufferPos += size;
245       return result;
246     } else {
247       // Slow path:  Build a byte array first then copy it.
248       return ByteStringMicro.copyFrom(readRawBytes(size));
249     }
250   }
251 
252   /** Read a {@code uint32} field value from the stream. */
readUInt32()253   public int readUInt32() throws IOException {
254     return readRawVarint32();
255   }
256 
257   /**
258    * Read an enum field value from the stream.  Caller is responsible
259    * for converting the numeric value to an actual enum.
260    */
readEnum()261   public int readEnum() throws IOException {
262     return readRawVarint32();
263   }
264 
265   /** Read an {@code sfixed32} field value from the stream. */
readSFixed32()266   public int readSFixed32() throws IOException {
267     return readRawLittleEndian32();
268   }
269 
270   /** Read an {@code sfixed64} field value from the stream. */
readSFixed64()271   public long readSFixed64() throws IOException {
272     return readRawLittleEndian64();
273   }
274 
275   /** Read an {@code sint32} field value from the stream. */
readSInt32()276   public int readSInt32() throws IOException {
277     return decodeZigZag32(readRawVarint32());
278   }
279 
280   /** Read an {@code sint64} field value from the stream. */
readSInt64()281   public long readSInt64() throws IOException {
282     return decodeZigZag64(readRawVarint64());
283   }
284 
285   // =================================================================
286 
287   /**
288    * Read a raw Varint from the stream.  If larger than 32 bits, discard the
289    * upper bits.
290    */
readRawVarint32()291   public int readRawVarint32() throws IOException {
292     byte tmp = readRawByte();
293     if (tmp >= 0) {
294       return tmp;
295     }
296     int result = tmp & 0x7f;
297     if ((tmp = readRawByte()) >= 0) {
298       result |= tmp << 7;
299     } else {
300       result |= (tmp & 0x7f) << 7;
301       if ((tmp = readRawByte()) >= 0) {
302         result |= tmp << 14;
303       } else {
304         result |= (tmp & 0x7f) << 14;
305         if ((tmp = readRawByte()) >= 0) {
306           result |= tmp << 21;
307         } else {
308           result |= (tmp & 0x7f) << 21;
309           result |= (tmp = readRawByte()) << 28;
310           if (tmp < 0) {
311             // Discard upper 32 bits.
312             for (int i = 0; i < 5; i++) {
313               if (readRawByte() >= 0) {
314                 return result;
315               }
316             }
317             throw InvalidProtocolBufferMicroException.malformedVarint();
318           }
319         }
320       }
321     }
322     return result;
323   }
324 
325   /**
326    * Reads a varint from the input one byte at a time, so that it does not
327    * read any bytes after the end of the varint.  If you simply wrapped the
328    * stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
329    * then you would probably end up reading past the end of the varint since
330    * CodedInputStream buffers its input.
331    */
readRawVarint32(final InputStream input)332   static int readRawVarint32(final InputStream input) throws IOException {
333     int result = 0;
334     int offset = 0;
335     for (; offset < 32; offset += 7) {
336       final int b = input.read();
337       if (b == -1) {
338         throw InvalidProtocolBufferMicroException.truncatedMessage();
339       }
340       result |= (b & 0x7f) << offset;
341       if ((b & 0x80) == 0) {
342         return result;
343       }
344     }
345     // Keep reading up to 64 bits.
346     for (; offset < 64; offset += 7) {
347       final int b = input.read();
348       if (b == -1) {
349         throw InvalidProtocolBufferMicroException.truncatedMessage();
350       }
351       if ((b & 0x80) == 0) {
352         return result;
353       }
354     }
355     throw InvalidProtocolBufferMicroException.malformedVarint();
356   }
357 
358   /** Read a raw Varint from the stream. */
readRawVarint64()359   public long readRawVarint64() throws IOException {
360     int shift = 0;
361     long result = 0;
362     while (shift < 64) {
363       final byte b = readRawByte();
364       result |= (long)(b & 0x7F) << shift;
365       if ((b & 0x80) == 0) {
366         return result;
367       }
368       shift += 7;
369     }
370     throw InvalidProtocolBufferMicroException.malformedVarint();
371   }
372 
373   /** Read a 32-bit little-endian integer from the stream. */
readRawLittleEndian32()374   public int readRawLittleEndian32() throws IOException {
375     final byte b1 = readRawByte();
376     final byte b2 = readRawByte();
377     final byte b3 = readRawByte();
378     final byte b4 = readRawByte();
379     return ((b1 & 0xff)      ) |
380            ((b2 & 0xff) <<  8) |
381            ((b3 & 0xff) << 16) |
382            ((b4 & 0xff) << 24);
383   }
384 
385   /** Read a 64-bit little-endian integer from the stream. */
readRawLittleEndian64()386   public long readRawLittleEndian64() throws IOException {
387     final byte b1 = readRawByte();
388     final byte b2 = readRawByte();
389     final byte b3 = readRawByte();
390     final byte b4 = readRawByte();
391     final byte b5 = readRawByte();
392     final byte b6 = readRawByte();
393     final byte b7 = readRawByte();
394     final byte b8 = readRawByte();
395     return (((long)b1 & 0xff)      ) |
396            (((long)b2 & 0xff) <<  8) |
397            (((long)b3 & 0xff) << 16) |
398            (((long)b4 & 0xff) << 24) |
399            (((long)b5 & 0xff) << 32) |
400            (((long)b6 & 0xff) << 40) |
401            (((long)b7 & 0xff) << 48) |
402            (((long)b8 & 0xff) << 56);
403   }
404 
405   /**
406    * Decode a ZigZag-encoded 32-bit value.  ZigZag encodes signed integers
407    * into values that can be efficiently encoded with varint.  (Otherwise,
408    * negative values must be sign-extended to 64 bits to be varint encoded,
409    * thus always taking 10 bytes on the wire.)
410    *
411    * @param n An unsigned 32-bit integer, stored in a signed int because
412    *          Java has no explicit unsigned support.
413    * @return A signed 32-bit integer.
414    */
decodeZigZag32(final int n)415   public static int decodeZigZag32(final int n) {
416     return (n >>> 1) ^ -(n & 1);
417   }
418 
419   /**
420    * Decode a ZigZag-encoded 64-bit value.  ZigZag encodes signed integers
421    * into values that can be efficiently encoded with varint.  (Otherwise,
422    * negative values must be sign-extended to 64 bits to be varint encoded,
423    * thus always taking 10 bytes on the wire.)
424    *
425    * @param n An unsigned 64-bit integer, stored in a signed int because
426    *          Java has no explicit unsigned support.
427    * @return A signed 64-bit integer.
428    */
decodeZigZag64(final long n)429   public static long decodeZigZag64(final long n) {
430     return (n >>> 1) ^ -(n & 1);
431   }
432 
433   // -----------------------------------------------------------------
434 
435   private final byte[] buffer;
436   private int bufferSize;
437   private int bufferSizeAfterLimit;
438   private int bufferPos;
439   private final InputStream input;
440   private int lastTag;
441 
442   /**
443    * The total number of bytes read before the current buffer.  The total
444    * bytes read up to the current position can be computed as
445    * {@code totalBytesRetired + bufferPos}.
446    */
447   private int totalBytesRetired;
448 
449   /** The absolute position of the end of the current message. */
450   private int currentLimit = Integer.MAX_VALUE;
451 
452   /** See setRecursionLimit() */
453   private int recursionDepth;
454   private int recursionLimit = DEFAULT_RECURSION_LIMIT;
455 
456   /** See setSizeLimit() */
457   private int sizeLimit = DEFAULT_SIZE_LIMIT;
458 
459   private static final int DEFAULT_RECURSION_LIMIT = 64;
460   private static final int DEFAULT_SIZE_LIMIT = 64 << 20;  // 64MB
461   private static final int BUFFER_SIZE = 4096;
462 
CodedInputStreamMicro(final byte[] buffer, final int off, final int len)463   private CodedInputStreamMicro(final byte[] buffer, final int off, final int len) {
464     this.buffer = buffer;
465     bufferSize = off + len;
466     bufferPos = off;
467     input = null;
468   }
469 
CodedInputStreamMicro(final InputStream input)470   private CodedInputStreamMicro(final InputStream input) {
471     buffer = new byte[BUFFER_SIZE];
472     bufferSize = 0;
473     bufferPos = 0;
474     this.input = input;
475   }
476 
477   /**
478    * Set the maximum message recursion depth.  In order to prevent malicious
479    * messages from causing stack overflows, {@code CodedInputStream} limits
480    * how deeply messages may be nested.  The default limit is 64.
481    *
482    * @return the old limit.
483    */
setRecursionLimit(final int limit)484   public int setRecursionLimit(final int limit) {
485     if (limit < 0) {
486       throw new IllegalArgumentException(
487         "Recursion limit cannot be negative: " + limit);
488     }
489     final int oldLimit = recursionLimit;
490     recursionLimit = limit;
491     return oldLimit;
492   }
493 
494   /**
495    * Set the maximum message size.  In order to prevent malicious
496    * messages from exhausting memory or causing integer overflows,
497    * {@code CodedInputStream} limits how large a message may be.
498    * The default limit is 64MB.  You should set this limit as small
499    * as you can without harming your app's functionality.  Note that
500    * size limits only apply when reading from an {@code InputStream}, not
501    * when constructed around a raw byte array (nor with
502    * {@link ByteStringMicro#newCodedInput}).
503    * <p>
504    * If you want to read several messages from a single CodedInputStream, you
505    * could call {@link #resetSizeCounter()} after each one to avoid hitting the
506    * size limit.
507    *
508    * @return the old limit.
509    */
setSizeLimit(final int limit)510   public int setSizeLimit(final int limit) {
511     if (limit < 0) {
512       throw new IllegalArgumentException(
513         "Size limit cannot be negative: " + limit);
514     }
515     final int oldLimit = sizeLimit;
516     sizeLimit = limit;
517     return oldLimit;
518   }
519 
520   /**
521    * Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
522    */
resetSizeCounter()523   public void resetSizeCounter() {
524     totalBytesRetired = 0;
525   }
526 
527   /**
528    * Sets {@code currentLimit} to (current position) + {@code byteLimit}.  This
529    * is called when descending into a length-delimited embedded message.
530    *
531    * @return the old limit.
532    */
pushLimit(int byteLimit)533   public int pushLimit(int byteLimit) throws InvalidProtocolBufferMicroException {
534     if (byteLimit < 0) {
535       throw InvalidProtocolBufferMicroException.negativeSize();
536     }
537     byteLimit += totalBytesRetired + bufferPos;
538     final int oldLimit = currentLimit;
539     if (byteLimit > oldLimit) {
540       throw InvalidProtocolBufferMicroException.truncatedMessage();
541     }
542     currentLimit = byteLimit;
543 
544     recomputeBufferSizeAfterLimit();
545 
546     return oldLimit;
547   }
548 
recomputeBufferSizeAfterLimit()549   private void recomputeBufferSizeAfterLimit() {
550     bufferSize += bufferSizeAfterLimit;
551     final int bufferEnd = totalBytesRetired + bufferSize;
552     if (bufferEnd > currentLimit) {
553       // Limit is in current buffer.
554       bufferSizeAfterLimit = bufferEnd - currentLimit;
555       bufferSize -= bufferSizeAfterLimit;
556     } else {
557       bufferSizeAfterLimit = 0;
558     }
559   }
560 
561   /**
562    * Discards the current limit, returning to the previous limit.
563    *
564    * @param oldLimit The old limit, as returned by {@code pushLimit}.
565    */
popLimit(final int oldLimit)566   public void popLimit(final int oldLimit) {
567     currentLimit = oldLimit;
568     recomputeBufferSizeAfterLimit();
569   }
570 
571   /**
572    * Returns the number of bytes to be read before the current limit.
573    * If no limit is set, returns -1.
574    */
getBytesUntilLimit()575   public int getBytesUntilLimit() {
576     if (currentLimit == Integer.MAX_VALUE) {
577       return -1;
578     }
579 
580     final int currentAbsolutePosition = totalBytesRetired + bufferPos;
581     return currentLimit - currentAbsolutePosition;
582   }
583 
584   /**
585    * Returns true if the stream has reached the end of the input.  This is the
586    * case if either the end of the underlying input source has been reached or
587    * if the stream has reached a limit created using {@link #pushLimit(int)}.
588    */
isAtEnd()589   public boolean isAtEnd() throws IOException {
590     return bufferPos == bufferSize && !refillBuffer(false);
591   }
592 
593   /**
594    * Called with {@code this.buffer} is empty to read more bytes from the
595    * input.  If {@code mustSucceed} is true, refillBuffer() gurantees that
596    * either there will be at least one byte in the buffer when it returns
597    * or it will throw an exception.  If {@code mustSucceed} is false,
598    * refillBuffer() returns false if no more bytes were available.
599    */
refillBuffer(final boolean mustSucceed)600   private boolean refillBuffer(final boolean mustSucceed) throws IOException {
601     if (bufferPos < bufferSize) {
602       throw new IllegalStateException(
603         "refillBuffer() called when buffer wasn't empty.");
604     }
605 
606     if (totalBytesRetired + bufferSize == currentLimit) {
607       // Oops, we hit a limit.
608       if (mustSucceed) {
609         throw InvalidProtocolBufferMicroException.truncatedMessage();
610       } else {
611         return false;
612       }
613     }
614 
615     totalBytesRetired += bufferSize;
616 
617     bufferPos = 0;
618     bufferSize = (input == null) ? -1 : input.read(buffer);
619     if (bufferSize == 0 || bufferSize < -1) {
620       throw new IllegalStateException(
621           "InputStream#read(byte[]) returned invalid result: " + bufferSize +
622           "\nThe InputStream implementation is buggy.");
623     }
624     if (bufferSize == -1) {
625       bufferSize = 0;
626       if (mustSucceed) {
627         throw InvalidProtocolBufferMicroException.truncatedMessage();
628       } else {
629         return false;
630       }
631     } else {
632       recomputeBufferSizeAfterLimit();
633       final int totalBytesRead =
634         totalBytesRetired + bufferSize + bufferSizeAfterLimit;
635       if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
636         throw InvalidProtocolBufferMicroException.sizeLimitExceeded();
637       }
638       return true;
639     }
640   }
641 
642   /**
643    * Read one byte from the input.
644    *
645    * @throws InvalidProtocolBufferMicroException The end of the stream or the current
646    *                                        limit was reached.
647    */
readRawByte()648   public byte readRawByte() throws IOException {
649     if (bufferPos == bufferSize) {
650       refillBuffer(true);
651     }
652     return buffer[bufferPos++];
653   }
654 
655   /**
656    * Read a fixed size of bytes from the input.
657    *
658    * @throws InvalidProtocolBufferMicroException The end of the stream or the current
659    *                                        limit was reached.
660    */
readRawBytes(final int size)661   public byte[] readRawBytes(final int size) throws IOException {
662     if (size < 0) {
663       throw InvalidProtocolBufferMicroException.negativeSize();
664     }
665 
666     if (totalBytesRetired + bufferPos + size > currentLimit) {
667       // Read to the end of the stream anyway.
668       skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
669       // Then fail.
670       throw InvalidProtocolBufferMicroException.truncatedMessage();
671     }
672 
673     if (size <= bufferSize - bufferPos) {
674       // We have all the bytes we need already.
675       final byte[] bytes = new byte[size];
676       System.arraycopy(buffer, bufferPos, bytes, 0, size);
677       bufferPos += size;
678       return bytes;
679     } else if (size < BUFFER_SIZE) {
680       // Reading more bytes than are in the buffer, but not an excessive number
681       // of bytes.  We can safely allocate the resulting array ahead of time.
682 
683       // First copy what we have.
684       final byte[] bytes = new byte[size];
685       int pos = bufferSize - bufferPos;
686       System.arraycopy(buffer, bufferPos, bytes, 0, pos);
687       bufferPos = bufferSize;
688 
689       // We want to use refillBuffer() and then copy from the buffer into our
690       // byte array rather than reading directly into our byte array because
691       // the input may be unbuffered.
692       refillBuffer(true);
693 
694       while (size - pos > bufferSize) {
695         System.arraycopy(buffer, 0, bytes, pos, bufferSize);
696         pos += bufferSize;
697         bufferPos = bufferSize;
698         refillBuffer(true);
699       }
700 
701       System.arraycopy(buffer, 0, bytes, pos, size - pos);
702       bufferPos = size - pos;
703 
704       return bytes;
705     } else {
706       // The size is very large.  For security reasons, we can't allocate the
707       // entire byte array yet.  The size comes directly from the input, so a
708       // maliciously-crafted message could provide a bogus very large size in
709       // order to trick the app into allocating a lot of memory.  We avoid this
710       // by allocating and reading only a small chunk at a time, so that the
711       // malicious message must actually *be* extremely large to cause
712       // problems.  Meanwhile, we limit the allowed size of a message elsewhere.
713 
714       // Remember the buffer markers since we'll have to copy the bytes out of
715       // it later.
716       final int originalBufferPos = bufferPos;
717       final int originalBufferSize = bufferSize;
718 
719       // Mark the current buffer consumed.
720       totalBytesRetired += bufferSize;
721       bufferPos = 0;
722       bufferSize = 0;
723 
724       // Read all the rest of the bytes we need.
725       int sizeLeft = size - (originalBufferSize - originalBufferPos);
726 
727       // For compatibility with Java 1.3 use Vector
728       final java.util.Vector chunks = new java.util.Vector();
729 
730       while (sizeLeft > 0) {
731         final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
732         int pos = 0;
733         while (pos < chunk.length) {
734           final int n = (input == null) ? -1 :
735             input.read(chunk, pos, chunk.length - pos);
736           if (n == -1) {
737             throw InvalidProtocolBufferMicroException.truncatedMessage();
738           }
739           totalBytesRetired += n;
740           pos += n;
741         }
742         sizeLeft -= chunk.length;
743         chunks.addElement(chunk);
744       }
745 
746       // OK, got everything.  Now concatenate it all into one buffer.
747       final byte[] bytes = new byte[size];
748 
749       // Start by copying the leftover bytes from this.buffer.
750       int pos = originalBufferSize - originalBufferPos;
751       System.arraycopy(buffer, originalBufferPos, bytes, 0, pos);
752 
753       // And now all the chunks.
754       for (int i = 0; i < chunks.size(); i++) {
755         byte [] chunk = (byte [])chunks.elementAt(i);
756         System.arraycopy(chunk, 0, bytes, pos, chunk.length);
757         pos += chunk.length;
758       }
759 
760       // Done.
761       return bytes;
762     }
763   }
764 
765   /**
766    * Reads and discards {@code size} bytes.
767    *
768    * @throws InvalidProtocolBufferMicroException The end of the stream or the current
769    *                                        limit was reached.
770    */
skipRawBytes(final int size)771   public void skipRawBytes(final int size) throws IOException {
772     if (size < 0) {
773       throw InvalidProtocolBufferMicroException.negativeSize();
774     }
775 
776     if (totalBytesRetired + bufferPos + size > currentLimit) {
777       // Read to the end of the stream anyway.
778       skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
779       // Then fail.
780       throw InvalidProtocolBufferMicroException.truncatedMessage();
781     }
782 
783     if (size <= bufferSize - bufferPos) {
784       // We have all the bytes we need already.
785       bufferPos += size;
786     } else {
787       // Skipping more bytes than are in the buffer.  First skip what we have.
788       int pos = bufferSize - bufferPos;
789       totalBytesRetired += bufferSize;
790       bufferPos = 0;
791       bufferSize = 0;
792 
793       // Then skip directly from the InputStream for the rest.
794       while (pos < size) {
795         final int n = (input == null) ? -1 : (int) input.skip(size - pos);
796         if (n <= 0) {
797           throw InvalidProtocolBufferMicroException.truncatedMessage();
798         }
799         pos += n;
800         totalBytesRetired += n;
801       }
802     }
803   }
804 }
805